~ubuntu-branches/ubuntu/raring/virtualbox-ose/raring

« back to all changes in this revision

Viewing changes to src/VBox/Main/src-server/MachineImpl.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-30 23:27:25 UTC
  • mfrom: (0.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20110130232725-2ouajjd2ggdet0zd
Tags: 4.0.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Drop *-source packages.
* Drop ubuntu-01-fix-build-gcc45.patch, fixed upstream.
* Drop ubuntu-02-as-needed.patch, added to the Debian package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: MachineImpl.cpp 35610 2011-01-18 14:24:36Z vboxsync $ */
 
2
/** @file
 
3
 * Implementation of IMachine in VBoxSVC.
 
4
 */
 
5
 
 
6
/*
 
7
 * Copyright (C) 2006-2011 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
 
 
53
#ifdef VBOX_WITH_USB
 
54
# include "USBProxyService.h"
 
55
#endif
 
56
 
 
57
#include "AutoCaller.h"
 
58
#include "Performance.h"
 
59
 
 
60
#include <iprt/asm.h>
 
61
#include <iprt/path.h>
 
62
#include <iprt/dir.h>
 
63
#include <iprt/env.h>
 
64
#include <iprt/lockvalidator.h>
 
65
#include <iprt/process.h>
 
66
#include <iprt/cpp/utils.h>
 
67
#include <iprt/cpp/xml.h>               /* xml::XmlFileWriter::s_psz*Suff. */
 
68
#include <iprt/string.h>
 
69
 
 
70
#include <VBox/com/array.h>
 
71
 
 
72
#include <VBox/err.h>
 
73
#include <VBox/param.h>
 
74
#include <VBox/settings.h>
 
75
#include <VBox/vmm/ssm.h>
 
76
 
 
77
#ifdef VBOX_WITH_GUEST_PROPS
 
78
# include <VBox/HostServices/GuestPropertySvc.h>
 
79
# include <VBox/com/array.h>
 
80
#endif
 
81
 
 
82
#include "VBox/com/MultiResult.h"
 
83
 
 
84
#include <algorithm>
 
85
 
 
86
#include <typeinfo>
 
87
 
 
88
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
 
89
# define HOSTSUFF_EXE ".exe"
 
90
#else /* !RT_OS_WINDOWS */
 
91
# define HOSTSUFF_EXE ""
 
92
#endif /* !RT_OS_WINDOWS */
 
93
 
 
94
// defines / prototypes
 
95
/////////////////////////////////////////////////////////////////////////////
 
96
 
 
97
/////////////////////////////////////////////////////////////////////////////
 
98
// Machine::Data structure
 
99
/////////////////////////////////////////////////////////////////////////////
 
100
 
 
101
Machine::Data::Data()
 
102
{
 
103
    mRegistered = FALSE;
 
104
    pMachineConfigFile = NULL;
 
105
    flModifications = 0;
 
106
    mAccessible = FALSE;
 
107
    /* mUuid is initialized in Machine::init() */
 
108
 
 
109
    mMachineState = MachineState_PoweredOff;
 
110
    RTTimeNow(&mLastStateChange);
 
111
 
 
112
    mMachineStateDeps = 0;
 
113
    mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
 
114
    mMachineStateChangePending = 0;
 
115
 
 
116
    mCurrentStateModified = TRUE;
 
117
    mGuestPropertiesModified = FALSE;
 
118
 
 
119
    mSession.mPid = NIL_RTPROCESS;
 
120
    mSession.mState = SessionState_Unlocked;
 
121
}
 
122
 
 
123
Machine::Data::~Data()
 
124
{
 
125
    if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
 
126
    {
 
127
        RTSemEventMultiDestroy(mMachineStateDepsSem);
 
128
        mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
 
129
    }
 
130
    if (pMachineConfigFile)
 
131
    {
 
132
        delete pMachineConfigFile;
 
133
        pMachineConfigFile = NULL;
 
134
    }
 
135
}
 
136
 
 
137
/////////////////////////////////////////////////////////////////////////////
 
138
// Machine::HWData structure
 
139
/////////////////////////////////////////////////////////////////////////////
 
140
 
 
141
Machine::HWData::HWData()
 
142
{
 
143
    /* default values for a newly created machine */
 
144
    mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
 
145
    mMemorySize = 128;
 
146
    mCPUCount = 1;
 
147
    mCPUHotPlugEnabled = false;
 
148
    mMemoryBalloonSize = 0;
 
149
    mPageFusionEnabled = false;
 
150
    mVRAMSize = 8;
 
151
    mAccelerate3DEnabled = false;
 
152
    mAccelerate2DVideoEnabled = false;
 
153
    mMonitorCount = 1;
 
154
    mHWVirtExEnabled = true;
 
155
    mHWVirtExNestedPagingEnabled = true;
 
156
#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
 
157
    mHWVirtExLargePagesEnabled = true;
 
158
#else
 
159
    /* Not supported on 32 bits hosts. */
 
160
    mHWVirtExLargePagesEnabled = false;
 
161
#endif
 
162
    mHWVirtExVPIDEnabled = true;
 
163
    mHWVirtExForceEnabled = false;
 
164
#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
 
165
    mHWVirtExExclusive = false;
 
166
#else
 
167
    mHWVirtExExclusive = true;
 
168
#endif
 
169
#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
 
170
    mPAEEnabled = true;
 
171
#else
 
172
    mPAEEnabled = false;
 
173
#endif
 
174
    mSyntheticCpu = false;
 
175
    mHpetEnabled = false;
 
176
 
 
177
    /* default boot order: floppy - DVD - HDD */
 
178
    mBootOrder[0] = DeviceType_Floppy;
 
179
    mBootOrder[1] = DeviceType_DVD;
 
180
    mBootOrder[2] = DeviceType_HardDisk;
 
181
    for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
 
182
        mBootOrder[i] = DeviceType_Null;
 
183
 
 
184
    mClipboardMode = ClipboardMode_Bidirectional;
 
185
    mGuestPropertyNotificationPatterns = "";
 
186
 
 
187
    mFirmwareType = FirmwareType_BIOS;
 
188
    mKeyboardHidType = KeyboardHidType_PS2Keyboard;
 
189
    mPointingHidType = PointingHidType_PS2Mouse;
 
190
    mChipsetType = ChipsetType_PIIX3;
 
191
 
 
192
    for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
 
193
        mCPUAttached[i] = false;
 
194
 
 
195
    mIoCacheEnabled = true;
 
196
    mIoCacheSize    = 5; /* 5MB */
 
197
 
 
198
    /* Maximum CPU execution cap by default. */
 
199
    mCpuExecutionCap = 100;
 
200
}
 
201
 
 
202
Machine::HWData::~HWData()
 
203
{
 
204
}
 
205
 
 
206
/////////////////////////////////////////////////////////////////////////////
 
207
// Machine::HDData structure
 
208
/////////////////////////////////////////////////////////////////////////////
 
209
 
 
210
Machine::MediaData::MediaData()
 
211
{
 
212
}
 
213
 
 
214
Machine::MediaData::~MediaData()
 
215
{
 
216
}
 
217
 
 
218
/////////////////////////////////////////////////////////////////////////////
 
219
// Machine class
 
220
/////////////////////////////////////////////////////////////////////////////
 
221
 
 
222
// constructor / destructor
 
223
/////////////////////////////////////////////////////////////////////////////
 
224
 
 
225
Machine::Machine()
 
226
    : mGuestHAL(NULL),
 
227
      mPeer(NULL),
 
228
      mParent(NULL)
 
229
{}
 
230
 
 
231
Machine::~Machine()
 
232
{}
 
233
 
 
234
HRESULT Machine::FinalConstruct()
 
235
{
 
236
    LogFlowThisFunc(("\n"));
 
237
    return S_OK;
 
238
}
 
239
 
 
240
void Machine::FinalRelease()
 
241
{
 
242
    LogFlowThisFunc(("\n"));
 
243
    uninit();
 
244
}
 
245
 
 
246
/**
 
247
 *  Initializes a new machine instance; this init() variant creates a new, empty machine.
 
248
 *  This gets called from VirtualBox::CreateMachine().
 
249
 *
 
250
 *  @param aParent      Associated parent object
 
251
 *  @param strConfigFile  Local file system path to the VM settings file (can
 
252
 *                      be relative to the VirtualBox config directory).
 
253
 *  @param strName      name for the machine
 
254
 *  @param aId          UUID for the new machine.
 
255
 *  @param aOsType      OS Type of this machine or NULL.
 
256
 *  @param fForceOverwrite Whether to overwrite an existing machine settings file.
 
257
 *
 
258
 *  @return  Success indicator. if not S_OK, the machine object is invalid
 
259
 */
 
260
HRESULT Machine::init(VirtualBox *aParent,
 
261
                      const Utf8Str &strConfigFile,
 
262
                      const Utf8Str &strName,
 
263
                      GuestOSType *aOsType,
 
264
                      const Guid &aId,
 
265
                      bool fForceOverwrite)
 
266
{
 
267
    LogFlowThisFuncEnter();
 
268
    LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
 
269
 
 
270
    /* Enclose the state transition NotReady->InInit->Ready */
 
271
    AutoInitSpan autoInitSpan(this);
 
272
    AssertReturn(autoInitSpan.isOk(), E_FAIL);
 
273
 
 
274
    HRESULT rc = initImpl(aParent, strConfigFile);
 
275
    if (FAILED(rc)) return rc;
 
276
 
 
277
    rc = tryCreateMachineConfigFile(fForceOverwrite);
 
278
    if (FAILED(rc)) return rc;
 
279
 
 
280
    if (SUCCEEDED(rc))
 
281
    {
 
282
        // create an empty machine config
 
283
        mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
 
284
 
 
285
        rc = initDataAndChildObjects();
 
286
    }
 
287
 
 
288
    if (SUCCEEDED(rc))
 
289
    {
 
290
        // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
 
291
        mData->mAccessible = TRUE;
 
292
 
 
293
        unconst(mData->mUuid) = aId;
 
294
 
 
295
        mUserData->s.strName = strName;
 
296
 
 
297
        // the "name sync" flag determines whether the machine directory gets renamed along
 
298
        // with the machine file; say so if the settings file name is the same as the
 
299
        // settings file parent directory (machine directory)
 
300
        mUserData->s.fNameSync = isInOwnDir();
 
301
 
 
302
        // initialize the default snapshots folder
 
303
        rc = COMSETTER(SnapshotFolder)(NULL);
 
304
        AssertComRC(rc);
 
305
 
 
306
        if (aOsType)
 
307
        {
 
308
            /* Store OS type */
 
309
            mUserData->s.strOsType = aOsType->id();
 
310
 
 
311
            /* Apply BIOS defaults */
 
312
            mBIOSSettings->applyDefaults(aOsType);
 
313
 
 
314
            /* Apply network adapters defaults */
 
315
            for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); ++slot)
 
316
                mNetworkAdapters[slot]->applyDefaults(aOsType);
 
317
 
 
318
            /* Apply serial port defaults */
 
319
            for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
 
320
                mSerialPorts[slot]->applyDefaults(aOsType);
 
321
        }
 
322
 
 
323
        /* commit all changes made during the initialization */
 
324
        commit();
 
325
    }
 
326
 
 
327
    /* Confirm a successful initialization when it's the case */
 
328
    if (SUCCEEDED(rc))
 
329
    {
 
330
        if (mData->mAccessible)
 
331
            autoInitSpan.setSucceeded();
 
332
        else
 
333
            autoInitSpan.setLimited();
 
334
    }
 
335
 
 
336
    LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
 
337
                     !!mUserData ? mUserData->s.strName.c_str() : "NULL",
 
338
                     mData->mRegistered,
 
339
                     mData->mAccessible,
 
340
                     rc));
 
341
 
 
342
    LogFlowThisFuncLeave();
 
343
 
 
344
    return rc;
 
345
}
 
346
 
 
347
/**
 
348
 *  Initializes a new instance with data from machine XML (formerly Init_Registered).
 
349
 *  Gets called in two modes:
 
350
 *
 
351
 *      -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
 
352
 *         UUID is specified and we mark the machine as "registered";
 
353
 *
 
354
 *      -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
 
355
 *         and the machine remains unregistered until RegisterMachine() is called.
 
356
 *
 
357
 *  @param aParent      Associated parent object
 
358
 *  @param aConfigFile  Local file system path to the VM settings file (can
 
359
 *                      be relative to the VirtualBox config directory).
 
360
 *  @param aId          UUID of the machine or NULL (see above).
 
361
 *
 
362
 *  @return  Success indicator. if not S_OK, the machine object is invalid
 
363
 */
 
364
HRESULT Machine::init(VirtualBox *aParent,
 
365
                      const Utf8Str &strConfigFile,
 
366
                      const Guid *aId)
 
367
{
 
368
    LogFlowThisFuncEnter();
 
369
    LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
 
370
 
 
371
    /* Enclose the state transition NotReady->InInit->Ready */
 
372
    AutoInitSpan autoInitSpan(this);
 
373
    AssertReturn(autoInitSpan.isOk(), E_FAIL);
 
374
 
 
375
    HRESULT rc = initImpl(aParent, strConfigFile);
 
376
    if (FAILED(rc)) return rc;
 
377
 
 
378
    if (aId)
 
379
    {
 
380
        // loading a registered VM:
 
381
        unconst(mData->mUuid) = *aId;
 
382
        mData->mRegistered = TRUE;
 
383
        // now load the settings from XML:
 
384
        rc = registeredInit();
 
385
            // this calls initDataAndChildObjects() and loadSettings()
 
386
    }
 
387
    else
 
388
    {
 
389
        // opening an unregistered VM (VirtualBox::OpenMachine()):
 
390
        rc = initDataAndChildObjects();
 
391
 
 
392
        if (SUCCEEDED(rc))
 
393
        {
 
394
            // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
 
395
            mData->mAccessible = TRUE;
 
396
 
 
397
            try
 
398
            {
 
399
                // load and parse machine XML; this will throw on XML or logic errors
 
400
                mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
 
401
 
 
402
                // reject VM UUID duplicates, they can happen if someone
 
403
                // tries to register an already known VM config again
 
404
                if (aParent->findMachine(mData->pMachineConfigFile->uuid,
 
405
                                         true /* fPermitInaccessible */,
 
406
                                         false /* aDoSetError */,
 
407
                                         NULL) != VBOX_E_OBJECT_NOT_FOUND)
 
408
                {
 
409
                    throw setError(E_FAIL,
 
410
                                   tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
 
411
                                   mData->m_strConfigFile.c_str());
 
412
                }
 
413
 
 
414
                // use UUID from machine config
 
415
                unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
 
416
 
 
417
                rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
 
418
                                                 NULL /* puuidRegistry */);
 
419
                if (FAILED(rc)) throw rc;
 
420
 
 
421
                commit();
 
422
            }
 
423
            catch (HRESULT err)
 
424
            {
 
425
                /* we assume that error info is set by the thrower */
 
426
                rc = err;
 
427
            }
 
428
            catch (...)
 
429
            {
 
430
                rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
 
431
            }
 
432
        }
 
433
    }
 
434
 
 
435
    /* Confirm a successful initialization when it's the case */
 
436
    if (SUCCEEDED(rc))
 
437
    {
 
438
        if (mData->mAccessible)
 
439
            autoInitSpan.setSucceeded();
 
440
        else
 
441
        {
 
442
            autoInitSpan.setLimited();
 
443
 
 
444
            // uninit media from this machine's media registry, or else
 
445
            // reloading the settings will fail
 
446
            mParent->unregisterMachineMedia(getId());
 
447
        }
 
448
    }
 
449
 
 
450
    LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
 
451
                      "rc=%08X\n",
 
452
                      !!mUserData ? mUserData->s.strName.c_str() : "NULL",
 
453
                      mData->mRegistered, mData->mAccessible, rc));
 
454
 
 
455
    LogFlowThisFuncLeave();
 
456
 
 
457
    return rc;
 
458
}
 
459
 
 
460
/**
 
461
 *  Initializes a new instance from a machine config that is already in memory
 
462
 *  (import OVF case). Since we are importing, the UUID in the machine
 
463
 *  config is ignored and we always generate a fresh one.
 
464
 *
 
465
 *  @param strName  Name for the new machine; this overrides what is specified in config and is used
 
466
 *                  for the settings file as well.
 
467
 *  @param config   Machine configuration loaded and parsed from XML.
 
468
 *
 
469
 *  @return  Success indicator. if not S_OK, the machine object is invalid
 
470
 */
 
471
HRESULT Machine::init(VirtualBox *aParent,
 
472
                      const Utf8Str &strName,
 
473
                      const settings::MachineConfigFile &config)
 
474
{
 
475
    LogFlowThisFuncEnter();
 
476
 
 
477
    /* Enclose the state transition NotReady->InInit->Ready */
 
478
    AutoInitSpan autoInitSpan(this);
 
479
    AssertReturn(autoInitSpan.isOk(), E_FAIL);
 
480
 
 
481
    Utf8Str strConfigFile;
 
482
    aParent->getDefaultMachineFolder(strConfigFile);
 
483
    strConfigFile.append(RTPATH_DELIMITER);
 
484
    strConfigFile.append(strName);
 
485
    strConfigFile.append(RTPATH_DELIMITER);
 
486
    strConfigFile.append(strName);
 
487
    strConfigFile.append(".vbox");
 
488
 
 
489
    HRESULT rc = initImpl(aParent, strConfigFile);
 
490
    if (FAILED(rc)) return rc;
 
491
 
 
492
    rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
 
493
    if (FAILED(rc)) return rc;
 
494
 
 
495
    rc = initDataAndChildObjects();
 
496
 
 
497
    if (SUCCEEDED(rc))
 
498
    {
 
499
        // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
 
500
        mData->mAccessible = TRUE;
 
501
 
 
502
        // create empty machine config for instance data
 
503
        mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
 
504
 
 
505
        // generate fresh UUID, ignore machine config
 
506
        unconst(mData->mUuid).create();
 
507
 
 
508
        rc = loadMachineDataFromSettings(config,
 
509
                                         &mData->mUuid); // puuidRegistry: initialize media with this registry ID
 
510
 
 
511
        // override VM name as well, it may be different
 
512
        mUserData->s.strName = strName;
 
513
 
 
514
        /* commit all changes made during the initialization */
 
515
        if (SUCCEEDED(rc))
 
516
            commit();
 
517
    }
 
518
 
 
519
    /* Confirm a successful initialization when it's the case */
 
520
    if (SUCCEEDED(rc))
 
521
    {
 
522
        if (mData->mAccessible)
 
523
            autoInitSpan.setSucceeded();
 
524
        else
 
525
        {
 
526
            autoInitSpan.setLimited();
 
527
 
 
528
            // uninit media from this machine's media registry, or else
 
529
            // reloading the settings will fail
 
530
            mParent->unregisterMachineMedia(getId());
 
531
        }
 
532
    }
 
533
 
 
534
    LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
 
535
                     "rc=%08X\n",
 
536
                      !!mUserData ? mUserData->s.strName.c_str() : "NULL",
 
537
                      mData->mRegistered, mData->mAccessible, rc));
 
538
 
 
539
    LogFlowThisFuncLeave();
 
540
 
 
541
    return rc;
 
542
}
 
543
 
 
544
/**
 
545
 * Shared code between the various init() implementations.
 
546
 * @param aParent
 
547
 * @return
 
548
 */
 
549
HRESULT Machine::initImpl(VirtualBox *aParent,
 
550
                          const Utf8Str &strConfigFile)
 
551
{
 
552
    LogFlowThisFuncEnter();
 
553
 
 
554
    AssertReturn(aParent, E_INVALIDARG);
 
555
    AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
 
556
 
 
557
    HRESULT rc = S_OK;
 
558
 
 
559
    /* share the parent weakly */
 
560
    unconst(mParent) = aParent;
 
561
 
 
562
    /* allocate the essential machine data structure (the rest will be
 
563
     * allocated later by initDataAndChildObjects() */
 
564
    mData.allocate();
 
565
 
 
566
    /* memorize the config file name (as provided) */
 
567
    mData->m_strConfigFile = strConfigFile;
 
568
 
 
569
    /* get the full file name */
 
570
    int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
 
571
    if (RT_FAILURE(vrc1))
 
572
        return setError(VBOX_E_FILE_ERROR,
 
573
                        tr("Invalid machine settings file name '%s' (%Rrc)"),
 
574
                        strConfigFile.c_str(),
 
575
                        vrc1);
 
576
 
 
577
    LogFlowThisFuncLeave();
 
578
 
 
579
    return rc;
 
580
}
 
581
 
 
582
/**
 
583
 * Tries to create a machine settings file in the path stored in the machine
 
584
 * instance data. Used when a new machine is created to fail gracefully if
 
585
 * the settings file could not be written (e.g. because machine dir is read-only).
 
586
 * @return
 
587
 */
 
588
HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
 
589
{
 
590
    HRESULT rc = S_OK;
 
591
 
 
592
    // when we create a new machine, we must be able to create the settings file
 
593
    RTFILE f = NIL_RTFILE;
 
594
    int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
 
595
    if (    RT_SUCCESS(vrc)
 
596
         || vrc == VERR_SHARING_VIOLATION
 
597
       )
 
598
    {
 
599
        if (RT_SUCCESS(vrc))
 
600
            RTFileClose(f);
 
601
        if (!fForceOverwrite)
 
602
            rc = setError(VBOX_E_FILE_ERROR,
 
603
                          tr("Machine settings file '%s' already exists"),
 
604
                          mData->m_strConfigFileFull.c_str());
 
605
        else
 
606
        {
 
607
            /* try to delete the config file, as otherwise the creation
 
608
             * of a new settings file will fail. */
 
609
            int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
 
610
            if (RT_FAILURE(vrc2))
 
611
                rc = setError(VBOX_E_FILE_ERROR,
 
612
                              tr("Could not delete the existing settings file '%s' (%Rrc)"),
 
613
                              mData->m_strConfigFileFull.c_str(), vrc2);
 
614
        }
 
615
    }
 
616
    else if (    vrc != VERR_FILE_NOT_FOUND
 
617
              && vrc != VERR_PATH_NOT_FOUND
 
618
            )
 
619
        rc = setError(VBOX_E_FILE_ERROR,
 
620
                      tr("Invalid machine settings file name '%s' (%Rrc)"),
 
621
                      mData->m_strConfigFileFull.c_str(),
 
622
                      vrc);
 
623
    return rc;
 
624
}
 
625
 
 
626
/**
 
627
 *  Initializes the registered machine by loading the settings file.
 
628
 *  This method is separated from #init() in order to make it possible to
 
629
 *  retry the operation after VirtualBox startup instead of refusing to
 
630
 *  startup the whole VirtualBox server in case if the settings file of some
 
631
 *  registered VM is invalid or inaccessible.
 
632
 *
 
633
 *  @note Must be always called from this object's write lock
 
634
 *        (unless called from #init() that doesn't need any locking).
 
635
 *  @note Locks the mUSBController method for writing.
 
636
 *  @note Subclasses must not call this method.
 
637
 */
 
638
HRESULT Machine::registeredInit()
 
639
{
 
640
    AssertReturn(!isSessionMachine(), E_FAIL);
 
641
    AssertReturn(!isSnapshotMachine(), E_FAIL);
 
642
    AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
 
643
    AssertReturn(!mData->mAccessible, E_FAIL);
 
644
 
 
645
    HRESULT rc = initDataAndChildObjects();
 
646
 
 
647
    if (SUCCEEDED(rc))
 
648
    {
 
649
        /* Temporarily reset the registered flag in order to let setters
 
650
         * potentially called from loadSettings() succeed (isMutable() used in
 
651
         * all setters will return FALSE for a Machine instance if mRegistered
 
652
         * is TRUE). */
 
653
        mData->mRegistered = FALSE;
 
654
 
 
655
        try
 
656
        {
 
657
            // load and parse machine XML; this will throw on XML or logic errors
 
658
            mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
 
659
 
 
660
            if (mData->mUuid != mData->pMachineConfigFile->uuid)
 
661
                throw setError(E_FAIL,
 
662
                               tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
 
663
                               mData->pMachineConfigFile->uuid.raw(),
 
664
                               mData->m_strConfigFileFull.c_str(),
 
665
                               mData->mUuid.toString().c_str(),
 
666
                               mParent->settingsFilePath().c_str());
 
667
 
 
668
            rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
 
669
                                             NULL /* const Guid *puuidRegistry */);
 
670
            if (FAILED(rc)) throw rc;
 
671
        }
 
672
        catch (HRESULT err)
 
673
        {
 
674
            /* we assume that error info is set by the thrower */
 
675
            rc = err;
 
676
        }
 
677
        catch (...)
 
678
        {
 
679
            rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
 
680
        }
 
681
 
 
682
        /* Restore the registered flag (even on failure) */
 
683
        mData->mRegistered = TRUE;
 
684
    }
 
685
 
 
686
    if (SUCCEEDED(rc))
 
687
    {
 
688
        /* Set mAccessible to TRUE only if we successfully locked and loaded
 
689
         * the settings file */
 
690
        mData->mAccessible = TRUE;
 
691
 
 
692
        /* commit all changes made during loading the settings file */
 
693
        commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
 
694
    }
 
695
    else
 
696
    {
 
697
        /* If the machine is registered, then, instead of returning a
 
698
         * failure, we mark it as inaccessible and set the result to
 
699
         * success to give it a try later */
 
700
 
 
701
        /* fetch the current error info */
 
702
        mData->mAccessError = com::ErrorInfo();
 
703
        LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
 
704
                    mData->mUuid.raw(),
 
705
                    mData->mAccessError.getText().raw()));
 
706
 
 
707
        /* rollback all changes */
 
708
        rollback(false /* aNotify */);
 
709
 
 
710
        // uninit media from this machine's media registry, or else
 
711
        // reloading the settings will fail
 
712
        mParent->unregisterMachineMedia(getId());
 
713
 
 
714
        /* uninitialize the common part to make sure all data is reset to
 
715
         * default (null) values */
 
716
        uninitDataAndChildObjects();
 
717
 
 
718
        rc = S_OK;
 
719
    }
 
720
 
 
721
    return rc;
 
722
}
 
723
 
 
724
/**
 
725
 *  Uninitializes the instance.
 
726
 *  Called either from FinalRelease() or by the parent when it gets destroyed.
 
727
 *
 
728
 *  @note The caller of this method must make sure that this object
 
729
 *  a) doesn't have active callers on the current thread and b) is not locked
 
730
 *  by the current thread; otherwise uninit() will hang either a) due to
 
731
 *  AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
 
732
 *  a dead-lock caused by this thread waiting for all callers on the other
 
733
 *  threads are done but preventing them from doing so by holding a lock.
 
734
 */
 
735
void Machine::uninit()
 
736
{
 
737
    LogFlowThisFuncEnter();
 
738
 
 
739
    Assert(!isWriteLockOnCurrentThread());
 
740
 
 
741
    /* Enclose the state transition Ready->InUninit->NotReady */
 
742
    AutoUninitSpan autoUninitSpan(this);
 
743
    if (autoUninitSpan.uninitDone())
 
744
        return;
 
745
 
 
746
    Assert(!isSnapshotMachine());
 
747
    Assert(!isSessionMachine());
 
748
    Assert(!!mData);
 
749
 
 
750
    LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
 
751
    LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
 
752
 
 
753
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
754
 
 
755
    if (!mData->mSession.mMachine.isNull())
 
756
    {
 
757
        /* Theoretically, this can only happen if the VirtualBox server has been
 
758
         * terminated while there were clients running that owned open direct
 
759
         * sessions. Since in this case we are definitely called by
 
760
         * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
 
761
         * won't happen on the client watcher thread (because it does
 
762
         * VirtualBox::addCaller() for the duration of the
 
763
         * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
 
764
         * cannot happen until the VirtualBox caller is released). This is
 
765
         * important, because SessionMachine::uninit() cannot correctly operate
 
766
         * after we return from this method (it expects the Machine instance is
 
767
         * still valid). We'll call it ourselves below.
 
768
         */
 
769
        LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
 
770
                            (SessionMachine*)mData->mSession.mMachine));
 
771
 
 
772
        if (Global::IsOnlineOrTransient(mData->mMachineState))
 
773
        {
 
774
            LogWarningThisFunc(("Setting state to Aborted!\n"));
 
775
            /* set machine state using SessionMachine reimplementation */
 
776
            static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
 
777
        }
 
778
 
 
779
        /*
 
780
         *  Uninitialize SessionMachine using public uninit() to indicate
 
781
         *  an unexpected uninitialization.
 
782
         */
 
783
        mData->mSession.mMachine->uninit();
 
784
        /* SessionMachine::uninit() must set mSession.mMachine to null */
 
785
        Assert(mData->mSession.mMachine.isNull());
 
786
    }
 
787
 
 
788
    // uninit media from this machine's media registry, if they're still there
 
789
    Guid uuidMachine(getId());
 
790
    if (!uuidMachine.isEmpty())     // can be empty if we're called from a failure of Machine::init
 
791
        mParent->unregisterMachineMedia(uuidMachine);
 
792
 
 
793
    /* the lock is no more necessary (SessionMachine is uninitialized) */
 
794
    alock.leave();
 
795
 
 
796
    // has machine been modified?
 
797
    if (mData->flModifications)
 
798
    {
 
799
        LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
 
800
        rollback(false /* aNotify */);
 
801
    }
 
802
 
 
803
    if (mData->mAccessible)
 
804
        uninitDataAndChildObjects();
 
805
 
 
806
    /* free the essential data structure last */
 
807
    mData.free();
 
808
 
 
809
    LogFlowThisFuncLeave();
 
810
}
 
811
 
 
812
// IMachine properties
 
813
/////////////////////////////////////////////////////////////////////////////
 
814
 
 
815
STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
 
816
{
 
817
    CheckComArgOutPointerValid(aParent);
 
818
 
 
819
    AutoLimitedCaller autoCaller(this);
 
820
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
821
 
 
822
    /* mParent is constant during life time, no need to lock */
 
823
    ComObjPtr<VirtualBox> pVirtualBox(mParent);
 
824
    pVirtualBox.queryInterfaceTo(aParent);
 
825
 
 
826
    return S_OK;
 
827
}
 
828
 
 
829
STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
 
830
{
 
831
    CheckComArgOutPointerValid(aAccessible);
 
832
 
 
833
    AutoLimitedCaller autoCaller(this);
 
834
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
835
 
 
836
    LogFlowThisFunc(("ENTER\n"));
 
837
 
 
838
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
839
 
 
840
    HRESULT rc = S_OK;
 
841
 
 
842
    if (!mData->mAccessible)
 
843
    {
 
844
        /* try to initialize the VM once more if not accessible */
 
845
 
 
846
        AutoReinitSpan autoReinitSpan(this);
 
847
        AssertReturn(autoReinitSpan.isOk(), E_FAIL);
 
848
 
 
849
#ifdef DEBUG
 
850
        LogFlowThisFunc(("Dumping media backreferences\n"));
 
851
        mParent->dumpAllBackRefs();
 
852
#endif
 
853
 
 
854
        if (mData->pMachineConfigFile)
 
855
        {
 
856
            // reset the XML file to force loadSettings() (called from registeredInit())
 
857
            // to parse it again; the file might have changed
 
858
            delete mData->pMachineConfigFile;
 
859
            mData->pMachineConfigFile = NULL;
 
860
        }
 
861
 
 
862
        rc = registeredInit();
 
863
 
 
864
        if (SUCCEEDED(rc) && mData->mAccessible)
 
865
        {
 
866
            autoReinitSpan.setSucceeded();
 
867
 
 
868
            /* make sure interesting parties will notice the accessibility
 
869
             * state change */
 
870
            mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
 
871
            mParent->onMachineDataChange(mData->mUuid);
 
872
        }
 
873
    }
 
874
 
 
875
    if (SUCCEEDED(rc))
 
876
        *aAccessible = mData->mAccessible;
 
877
 
 
878
    LogFlowThisFuncLeave();
 
879
 
 
880
    return rc;
 
881
}
 
882
 
 
883
STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
 
884
{
 
885
    CheckComArgOutPointerValid(aAccessError);
 
886
 
 
887
    AutoLimitedCaller autoCaller(this);
 
888
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
889
 
 
890
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
891
 
 
892
    if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
 
893
    {
 
894
        /* return shortly */
 
895
        aAccessError = NULL;
 
896
        return S_OK;
 
897
    }
 
898
 
 
899
    HRESULT rc = S_OK;
 
900
 
 
901
    ComObjPtr<VirtualBoxErrorInfo> errorInfo;
 
902
    rc = errorInfo.createObject();
 
903
    if (SUCCEEDED(rc))
 
904
    {
 
905
        errorInfo->init(mData->mAccessError.getResultCode(),
 
906
                        mData->mAccessError.getInterfaceID().ref(),
 
907
                        Utf8Str(mData->mAccessError.getComponent()).c_str(),
 
908
                        Utf8Str(mData->mAccessError.getText()));
 
909
        rc = errorInfo.queryInterfaceTo(aAccessError);
 
910
    }
 
911
 
 
912
    return rc;
 
913
}
 
914
 
 
915
STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
 
916
{
 
917
    CheckComArgOutPointerValid(aName);
 
918
 
 
919
    AutoCaller autoCaller(this);
 
920
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
921
 
 
922
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
923
 
 
924
    mUserData->s.strName.cloneTo(aName);
 
925
 
 
926
    return S_OK;
 
927
}
 
928
 
 
929
STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
 
930
{
 
931
    CheckComArgStrNotEmptyOrNull(aName);
 
932
 
 
933
    AutoCaller autoCaller(this);
 
934
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
935
 
 
936
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
937
 
 
938
    HRESULT rc = checkStateDependency(MutableStateDep);
 
939
    if (FAILED(rc)) return rc;
 
940
 
 
941
    setModified(IsModified_MachineData);
 
942
    mUserData.backup();
 
943
    mUserData->s.strName = aName;
 
944
 
 
945
    return S_OK;
 
946
}
 
947
 
 
948
STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
 
949
{
 
950
    CheckComArgOutPointerValid(aDescription);
 
951
 
 
952
    AutoCaller autoCaller(this);
 
953
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
954
 
 
955
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
956
 
 
957
    mUserData->s.strDescription.cloneTo(aDescription);
 
958
 
 
959
    return S_OK;
 
960
}
 
961
 
 
962
STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
 
963
{
 
964
    AutoCaller autoCaller(this);
 
965
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
966
 
 
967
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
968
 
 
969
    HRESULT rc = checkStateDependency(MutableStateDep);
 
970
    if (FAILED(rc)) return rc;
 
971
 
 
972
    setModified(IsModified_MachineData);
 
973
    mUserData.backup();
 
974
    mUserData->s.strDescription = aDescription;
 
975
 
 
976
    return S_OK;
 
977
}
 
978
 
 
979
STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
 
980
{
 
981
    CheckComArgOutPointerValid(aId);
 
982
 
 
983
    AutoLimitedCaller autoCaller(this);
 
984
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
985
 
 
986
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
987
 
 
988
    mData->mUuid.toUtf16().cloneTo(aId);
 
989
 
 
990
    return S_OK;
 
991
}
 
992
 
 
993
STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
 
994
{
 
995
    CheckComArgOutPointerValid(aOSTypeId);
 
996
 
 
997
    AutoCaller autoCaller(this);
 
998
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
999
 
 
1000
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1001
 
 
1002
    mUserData->s.strOsType.cloneTo(aOSTypeId);
 
1003
 
 
1004
    return S_OK;
 
1005
}
 
1006
 
 
1007
STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
 
1008
{
 
1009
    CheckComArgStrNotEmptyOrNull(aOSTypeId);
 
1010
 
 
1011
    AutoCaller autoCaller(this);
 
1012
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1013
 
 
1014
    /* look up the object by Id to check it is valid */
 
1015
    ComPtr<IGuestOSType> guestOSType;
 
1016
    HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
 
1017
    if (FAILED(rc)) return rc;
 
1018
 
 
1019
    /* when setting, always use the "etalon" value for consistency -- lookup
 
1020
     * by ID is case-insensitive and the input value may have different case */
 
1021
    Bstr osTypeId;
 
1022
    rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
 
1023
    if (FAILED(rc)) return rc;
 
1024
 
 
1025
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1026
 
 
1027
    rc = checkStateDependency(MutableStateDep);
 
1028
    if (FAILED(rc)) return rc;
 
1029
 
 
1030
    setModified(IsModified_MachineData);
 
1031
    mUserData.backup();
 
1032
    mUserData->s.strOsType = osTypeId;
 
1033
 
 
1034
    return S_OK;
 
1035
}
 
1036
 
 
1037
 
 
1038
STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
 
1039
{
 
1040
    CheckComArgOutPointerValid(aFirmwareType);
 
1041
 
 
1042
    AutoCaller autoCaller(this);
 
1043
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1044
 
 
1045
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1046
 
 
1047
    *aFirmwareType = mHWData->mFirmwareType;
 
1048
 
 
1049
    return S_OK;
 
1050
}
 
1051
 
 
1052
STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
 
1053
{
 
1054
    AutoCaller autoCaller(this);
 
1055
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1056
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1057
 
 
1058
    int rc = checkStateDependency(MutableStateDep);
 
1059
    if (FAILED(rc)) return rc;
 
1060
 
 
1061
    setModified(IsModified_MachineData);
 
1062
    mHWData.backup();
 
1063
    mHWData->mFirmwareType = aFirmwareType;
 
1064
 
 
1065
    return S_OK;
 
1066
}
 
1067
 
 
1068
STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
 
1069
{
 
1070
    CheckComArgOutPointerValid(aKeyboardHidType);
 
1071
 
 
1072
    AutoCaller autoCaller(this);
 
1073
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1074
 
 
1075
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1076
 
 
1077
    *aKeyboardHidType = mHWData->mKeyboardHidType;
 
1078
 
 
1079
    return S_OK;
 
1080
}
 
1081
 
 
1082
STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T  aKeyboardHidType)
 
1083
{
 
1084
    AutoCaller autoCaller(this);
 
1085
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1086
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1087
 
 
1088
    int rc = checkStateDependency(MutableStateDep);
 
1089
    if (FAILED(rc)) return rc;
 
1090
 
 
1091
    setModified(IsModified_MachineData);
 
1092
    mHWData.backup();
 
1093
    mHWData->mKeyboardHidType = aKeyboardHidType;
 
1094
 
 
1095
    return S_OK;
 
1096
}
 
1097
 
 
1098
STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
 
1099
{
 
1100
    CheckComArgOutPointerValid(aPointingHidType);
 
1101
 
 
1102
    AutoCaller autoCaller(this);
 
1103
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1104
 
 
1105
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1106
 
 
1107
    *aPointingHidType = mHWData->mPointingHidType;
 
1108
 
 
1109
    return S_OK;
 
1110
}
 
1111
 
 
1112
STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T  aPointingHidType)
 
1113
{
 
1114
    AutoCaller autoCaller(this);
 
1115
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1116
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1117
 
 
1118
    int rc = checkStateDependency(MutableStateDep);
 
1119
    if (FAILED(rc)) return rc;
 
1120
 
 
1121
    setModified(IsModified_MachineData);
 
1122
    mHWData.backup();
 
1123
    mHWData->mPointingHidType = aPointingHidType;
 
1124
 
 
1125
    return S_OK;
 
1126
}
 
1127
 
 
1128
STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
 
1129
{
 
1130
    CheckComArgOutPointerValid(aChipsetType);
 
1131
 
 
1132
    AutoCaller autoCaller(this);
 
1133
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1134
 
 
1135
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1136
 
 
1137
    *aChipsetType = mHWData->mChipsetType;
 
1138
 
 
1139
    return S_OK;
 
1140
}
 
1141
 
 
1142
STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
 
1143
{
 
1144
    AutoCaller autoCaller(this);
 
1145
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1146
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1147
 
 
1148
    int rc = checkStateDependency(MutableStateDep);
 
1149
    if (FAILED(rc)) return rc;
 
1150
 
 
1151
    setModified(IsModified_MachineData);
 
1152
    mHWData.backup();
 
1153
    mHWData->mChipsetType = aChipsetType;
 
1154
 
 
1155
    return S_OK;
 
1156
}
 
1157
 
 
1158
STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
 
1159
{
 
1160
    if (!aHWVersion)
 
1161
        return E_POINTER;
 
1162
 
 
1163
    AutoCaller autoCaller(this);
 
1164
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1165
 
 
1166
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1167
 
 
1168
    mHWData->mHWVersion.cloneTo(aHWVersion);
 
1169
 
 
1170
    return S_OK;
 
1171
}
 
1172
 
 
1173
STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
 
1174
{
 
1175
    /* check known version */
 
1176
    Utf8Str hwVersion = aHWVersion;
 
1177
    if (    hwVersion.compare("1") != 0
 
1178
        &&  hwVersion.compare("2") != 0)
 
1179
        return setError(E_INVALIDARG,
 
1180
                        tr("Invalid hardware version: %ls\n"), aHWVersion);
 
1181
 
 
1182
    AutoCaller autoCaller(this);
 
1183
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1184
 
 
1185
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1186
 
 
1187
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1188
    if (FAILED(rc)) return rc;
 
1189
 
 
1190
    setModified(IsModified_MachineData);
 
1191
    mHWData.backup();
 
1192
    mHWData->mHWVersion = hwVersion;
 
1193
 
 
1194
    return S_OK;
 
1195
}
 
1196
 
 
1197
STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
 
1198
{
 
1199
    CheckComArgOutPointerValid(aUUID);
 
1200
 
 
1201
    AutoCaller autoCaller(this);
 
1202
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1203
 
 
1204
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1205
 
 
1206
    if (!mHWData->mHardwareUUID.isEmpty())
 
1207
        mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
 
1208
    else
 
1209
        mData->mUuid.toUtf16().cloneTo(aUUID);
 
1210
 
 
1211
    return S_OK;
 
1212
}
 
1213
 
 
1214
STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
 
1215
{
 
1216
    Guid hardwareUUID(aUUID);
 
1217
    if (hardwareUUID.isEmpty())
 
1218
        return E_INVALIDARG;
 
1219
 
 
1220
    AutoCaller autoCaller(this);
 
1221
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1222
 
 
1223
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1224
 
 
1225
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1226
    if (FAILED(rc)) return rc;
 
1227
 
 
1228
    setModified(IsModified_MachineData);
 
1229
    mHWData.backup();
 
1230
    if (hardwareUUID == mData->mUuid)
 
1231
        mHWData->mHardwareUUID.clear();
 
1232
    else
 
1233
        mHWData->mHardwareUUID = hardwareUUID;
 
1234
 
 
1235
    return S_OK;
 
1236
}
 
1237
 
 
1238
STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
 
1239
{
 
1240
    if (!memorySize)
 
1241
        return E_POINTER;
 
1242
 
 
1243
    AutoCaller autoCaller(this);
 
1244
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1245
 
 
1246
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1247
 
 
1248
    *memorySize = mHWData->mMemorySize;
 
1249
 
 
1250
    return S_OK;
 
1251
}
 
1252
 
 
1253
STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
 
1254
{
 
1255
    /* check RAM limits */
 
1256
    if (    memorySize < MM_RAM_MIN_IN_MB
 
1257
         || memorySize > MM_RAM_MAX_IN_MB
 
1258
       )
 
1259
        return setError(E_INVALIDARG,
 
1260
                        tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
 
1261
                        memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
 
1262
 
 
1263
    AutoCaller autoCaller(this);
 
1264
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1265
 
 
1266
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1267
 
 
1268
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1269
    if (FAILED(rc)) return rc;
 
1270
 
 
1271
    setModified(IsModified_MachineData);
 
1272
    mHWData.backup();
 
1273
    mHWData->mMemorySize = memorySize;
 
1274
 
 
1275
    return S_OK;
 
1276
}
 
1277
 
 
1278
STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
 
1279
{
 
1280
    if (!CPUCount)
 
1281
        return E_POINTER;
 
1282
 
 
1283
    AutoCaller autoCaller(this);
 
1284
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1285
 
 
1286
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1287
 
 
1288
    *CPUCount = mHWData->mCPUCount;
 
1289
 
 
1290
    return S_OK;
 
1291
}
 
1292
 
 
1293
STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
 
1294
{
 
1295
    /* check CPU limits */
 
1296
    if (    CPUCount < SchemaDefs::MinCPUCount
 
1297
         || CPUCount > SchemaDefs::MaxCPUCount
 
1298
       )
 
1299
        return setError(E_INVALIDARG,
 
1300
                        tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
 
1301
                        CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
 
1302
 
 
1303
    AutoCaller autoCaller(this);
 
1304
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1305
 
 
1306
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1307
 
 
1308
    /* We cant go below the current number of CPUs if hotplug is enabled*/
 
1309
    if (mHWData->mCPUHotPlugEnabled)
 
1310
    {
 
1311
        for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
 
1312
        {
 
1313
            if (mHWData->mCPUAttached[idx])
 
1314
                return setError(E_INVALIDARG,
 
1315
                                tr(": %lu (must be higher than or equal to %lu)"),
 
1316
                                CPUCount, idx+1);
 
1317
        }
 
1318
    }
 
1319
 
 
1320
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1321
    if (FAILED(rc)) return rc;
 
1322
 
 
1323
    setModified(IsModified_MachineData);
 
1324
    mHWData.backup();
 
1325
    mHWData->mCPUCount = CPUCount;
 
1326
 
 
1327
    return S_OK;
 
1328
}
 
1329
 
 
1330
STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
 
1331
{
 
1332
    if (!aExecutionCap)
 
1333
        return E_POINTER;
 
1334
 
 
1335
    AutoCaller autoCaller(this);
 
1336
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1337
 
 
1338
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1339
 
 
1340
    *aExecutionCap = mHWData->mCpuExecutionCap;
 
1341
 
 
1342
    return S_OK;
 
1343
}
 
1344
 
 
1345
STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
 
1346
{
 
1347
    HRESULT rc = S_OK;
 
1348
 
 
1349
    /* check throttle limits */
 
1350
    if (    aExecutionCap < 1
 
1351
         || aExecutionCap > 100
 
1352
       )
 
1353
        return setError(E_INVALIDARG,
 
1354
                        tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
 
1355
                        aExecutionCap, 1, 100);
 
1356
 
 
1357
    AutoCaller autoCaller(this);
 
1358
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1359
 
 
1360
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1361
 
 
1362
    alock.release();
 
1363
    rc = onCPUExecutionCapChange(aExecutionCap);
 
1364
    alock.acquire();
 
1365
    if (FAILED(rc)) return rc;
 
1366
 
 
1367
    setModified(IsModified_MachineData);
 
1368
    mHWData.backup();
 
1369
    mHWData->mCpuExecutionCap = aExecutionCap;
 
1370
 
 
1371
    /* Save settings if online - todo why is this required?? */
 
1372
    if (Global::IsOnline(mData->mMachineState))
 
1373
        saveSettings(NULL);
 
1374
 
 
1375
    return S_OK;
 
1376
}
 
1377
 
 
1378
 
 
1379
STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
 
1380
{
 
1381
    if (!enabled)
 
1382
        return E_POINTER;
 
1383
 
 
1384
    AutoCaller autoCaller(this);
 
1385
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1386
 
 
1387
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1388
 
 
1389
    *enabled = mHWData->mCPUHotPlugEnabled;
 
1390
 
 
1391
    return S_OK;
 
1392
}
 
1393
 
 
1394
STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
 
1395
{
 
1396
    HRESULT rc = S_OK;
 
1397
 
 
1398
    AutoCaller autoCaller(this);
 
1399
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1400
 
 
1401
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1402
 
 
1403
    rc = checkStateDependency(MutableStateDep);
 
1404
    if (FAILED(rc)) return rc;
 
1405
 
 
1406
    if (mHWData->mCPUHotPlugEnabled != enabled)
 
1407
    {
 
1408
        if (enabled)
 
1409
        {
 
1410
            setModified(IsModified_MachineData);
 
1411
            mHWData.backup();
 
1412
 
 
1413
            /* Add the amount of CPUs currently attached */
 
1414
            for (unsigned i = 0; i < mHWData->mCPUCount; i++)
 
1415
            {
 
1416
                mHWData->mCPUAttached[i] = true;
 
1417
            }
 
1418
        }
 
1419
        else
 
1420
        {
 
1421
            /*
 
1422
             * We can disable hotplug only if the amount of maximum CPUs is equal
 
1423
             * to the amount of attached CPUs
 
1424
             */
 
1425
            unsigned cCpusAttached = 0;
 
1426
            unsigned iHighestId = 0;
 
1427
 
 
1428
            for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
 
1429
            {
 
1430
                if (mHWData->mCPUAttached[i])
 
1431
                {
 
1432
                    cCpusAttached++;
 
1433
                    iHighestId = i;
 
1434
                }
 
1435
            }
 
1436
 
 
1437
            if (   (cCpusAttached != mHWData->mCPUCount)
 
1438
                || (iHighestId >= mHWData->mCPUCount))
 
1439
                return setError(E_INVALIDARG,
 
1440
                                tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n"));
 
1441
 
 
1442
            setModified(IsModified_MachineData);
 
1443
            mHWData.backup();
 
1444
        }
 
1445
    }
 
1446
 
 
1447
    mHWData->mCPUHotPlugEnabled = enabled;
 
1448
 
 
1449
    return rc;
 
1450
}
 
1451
 
 
1452
STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
 
1453
{
 
1454
    CheckComArgOutPointerValid(enabled);
 
1455
 
 
1456
    AutoCaller autoCaller(this);
 
1457
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1458
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1459
 
 
1460
    *enabled = mHWData->mHpetEnabled;
 
1461
 
 
1462
    return S_OK;
 
1463
}
 
1464
 
 
1465
STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
 
1466
{
 
1467
    HRESULT rc = S_OK;
 
1468
 
 
1469
    AutoCaller autoCaller(this);
 
1470
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1471
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1472
 
 
1473
    rc = checkStateDependency(MutableStateDep);
 
1474
    if (FAILED(rc)) return rc;
 
1475
 
 
1476
    setModified(IsModified_MachineData);
 
1477
    mHWData.backup();
 
1478
 
 
1479
    mHWData->mHpetEnabled = enabled;
 
1480
 
 
1481
    return rc;
 
1482
}
 
1483
 
 
1484
STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
 
1485
{
 
1486
    if (!memorySize)
 
1487
        return E_POINTER;
 
1488
 
 
1489
    AutoCaller autoCaller(this);
 
1490
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1491
 
 
1492
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1493
 
 
1494
    *memorySize = mHWData->mVRAMSize;
 
1495
 
 
1496
    return S_OK;
 
1497
}
 
1498
 
 
1499
STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
 
1500
{
 
1501
    /* check VRAM limits */
 
1502
    if (memorySize < SchemaDefs::MinGuestVRAM ||
 
1503
        memorySize > SchemaDefs::MaxGuestVRAM)
 
1504
        return setError(E_INVALIDARG,
 
1505
                        tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
 
1506
                        memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
 
1507
 
 
1508
    AutoCaller autoCaller(this);
 
1509
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1510
 
 
1511
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1512
 
 
1513
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1514
    if (FAILED(rc)) return rc;
 
1515
 
 
1516
    setModified(IsModified_MachineData);
 
1517
    mHWData.backup();
 
1518
    mHWData->mVRAMSize = memorySize;
 
1519
 
 
1520
    return S_OK;
 
1521
}
 
1522
 
 
1523
/** @todo this method should not be public */
 
1524
STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
 
1525
{
 
1526
    if (!memoryBalloonSize)
 
1527
        return E_POINTER;
 
1528
 
 
1529
    AutoCaller autoCaller(this);
 
1530
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1531
 
 
1532
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1533
 
 
1534
    *memoryBalloonSize = mHWData->mMemoryBalloonSize;
 
1535
 
 
1536
    return S_OK;
 
1537
}
 
1538
 
 
1539
/**
 
1540
 * Set the memory balloon size.
 
1541
 *
 
1542
 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
 
1543
 * we have to make sure that we never call IGuest from here.
 
1544
 */
 
1545
STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
 
1546
{
 
1547
    /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
 
1548
#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
 
1549
    /* check limits */
 
1550
    if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
 
1551
        return setError(E_INVALIDARG,
 
1552
                        tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
 
1553
                        memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
 
1554
 
 
1555
    AutoCaller autoCaller(this);
 
1556
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1557
 
 
1558
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1559
 
 
1560
    setModified(IsModified_MachineData);
 
1561
    mHWData.backup();
 
1562
    mHWData->mMemoryBalloonSize = memoryBalloonSize;
 
1563
 
 
1564
    return S_OK;
 
1565
#else
 
1566
    NOREF(memoryBalloonSize);
 
1567
    return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
 
1568
#endif
 
1569
}
 
1570
 
 
1571
STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
 
1572
{
 
1573
    if (!enabled)
 
1574
        return E_POINTER;
 
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->mPageFusionEnabled;
 
1582
    return S_OK;
 
1583
}
 
1584
 
 
1585
STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
 
1586
{
 
1587
#ifdef VBOX_WITH_PAGE_SHARING
 
1588
    AutoCaller autoCaller(this);
 
1589
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1590
 
 
1591
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1592
 
 
1593
    /** @todo must support changes for running vms and keep this in sync with IGuest. */
 
1594
    setModified(IsModified_MachineData);
 
1595
    mHWData.backup();
 
1596
    mHWData->mPageFusionEnabled = enabled;
 
1597
    return S_OK;
 
1598
#else
 
1599
    NOREF(enabled);
 
1600
    return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
 
1601
#endif
 
1602
}
 
1603
 
 
1604
STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
 
1605
{
 
1606
    if (!enabled)
 
1607
        return E_POINTER;
 
1608
 
 
1609
    AutoCaller autoCaller(this);
 
1610
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1611
 
 
1612
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1613
 
 
1614
    *enabled = mHWData->mAccelerate3DEnabled;
 
1615
 
 
1616
    return S_OK;
 
1617
}
 
1618
 
 
1619
STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
 
1620
{
 
1621
    AutoCaller autoCaller(this);
 
1622
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1623
 
 
1624
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1625
 
 
1626
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1627
    if (FAILED(rc)) return rc;
 
1628
 
 
1629
    /** @todo check validity! */
 
1630
 
 
1631
    setModified(IsModified_MachineData);
 
1632
    mHWData.backup();
 
1633
    mHWData->mAccelerate3DEnabled = enable;
 
1634
 
 
1635
    return S_OK;
 
1636
}
 
1637
 
 
1638
 
 
1639
STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
 
1640
{
 
1641
    if (!enabled)
 
1642
        return E_POINTER;
 
1643
 
 
1644
    AutoCaller autoCaller(this);
 
1645
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1646
 
 
1647
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1648
 
 
1649
    *enabled = mHWData->mAccelerate2DVideoEnabled;
 
1650
 
 
1651
    return S_OK;
 
1652
}
 
1653
 
 
1654
STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
 
1655
{
 
1656
    AutoCaller autoCaller(this);
 
1657
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1658
 
 
1659
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1660
 
 
1661
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1662
    if (FAILED(rc)) return rc;
 
1663
 
 
1664
    /** @todo check validity! */
 
1665
 
 
1666
    setModified(IsModified_MachineData);
 
1667
    mHWData.backup();
 
1668
    mHWData->mAccelerate2DVideoEnabled = enable;
 
1669
 
 
1670
    return S_OK;
 
1671
}
 
1672
 
 
1673
STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
 
1674
{
 
1675
    if (!monitorCount)
 
1676
        return E_POINTER;
 
1677
 
 
1678
    AutoCaller autoCaller(this);
 
1679
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1680
 
 
1681
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1682
 
 
1683
    *monitorCount = mHWData->mMonitorCount;
 
1684
 
 
1685
    return S_OK;
 
1686
}
 
1687
 
 
1688
STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
 
1689
{
 
1690
    /* make sure monitor count is a sensible number */
 
1691
    if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
 
1692
        return setError(E_INVALIDARG,
 
1693
                        tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
 
1694
                        monitorCount, 1, SchemaDefs::MaxGuestMonitors);
 
1695
 
 
1696
    AutoCaller autoCaller(this);
 
1697
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1698
 
 
1699
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1700
 
 
1701
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1702
    if (FAILED(rc)) return rc;
 
1703
 
 
1704
    setModified(IsModified_MachineData);
 
1705
    mHWData.backup();
 
1706
    mHWData->mMonitorCount = monitorCount;
 
1707
 
 
1708
    return S_OK;
 
1709
}
 
1710
 
 
1711
STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
 
1712
{
 
1713
    if (!biosSettings)
 
1714
        return E_POINTER;
 
1715
 
 
1716
    AutoCaller autoCaller(this);
 
1717
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1718
 
 
1719
    /* mBIOSSettings is constant during life time, no need to lock */
 
1720
    mBIOSSettings.queryInterfaceTo(biosSettings);
 
1721
 
 
1722
    return S_OK;
 
1723
}
 
1724
 
 
1725
STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
 
1726
{
 
1727
    if (!aVal)
 
1728
        return E_POINTER;
 
1729
 
 
1730
    AutoCaller autoCaller(this);
 
1731
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1732
 
 
1733
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1734
 
 
1735
    switch(property)
 
1736
    {
 
1737
    case CPUPropertyType_PAE:
 
1738
        *aVal = mHWData->mPAEEnabled;
 
1739
        break;
 
1740
 
 
1741
    case CPUPropertyType_Synthetic:
 
1742
        *aVal = mHWData->mSyntheticCpu;
 
1743
        break;
 
1744
 
 
1745
    default:
 
1746
        return E_INVALIDARG;
 
1747
    }
 
1748
    return S_OK;
 
1749
}
 
1750
 
 
1751
STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
 
1752
{
 
1753
    AutoCaller autoCaller(this);
 
1754
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1755
 
 
1756
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1757
 
 
1758
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1759
    if (FAILED(rc)) return rc;
 
1760
 
 
1761
    switch(property)
 
1762
    {
 
1763
    case CPUPropertyType_PAE:
 
1764
        setModified(IsModified_MachineData);
 
1765
        mHWData.backup();
 
1766
        mHWData->mPAEEnabled = !!aVal;
 
1767
        break;
 
1768
 
 
1769
    case CPUPropertyType_Synthetic:
 
1770
        setModified(IsModified_MachineData);
 
1771
        mHWData.backup();
 
1772
        mHWData->mSyntheticCpu = !!aVal;
 
1773
        break;
 
1774
 
 
1775
    default:
 
1776
        return E_INVALIDARG;
 
1777
    }
 
1778
    return S_OK;
 
1779
}
 
1780
 
 
1781
STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
 
1782
{
 
1783
    CheckComArgOutPointerValid(aValEax);
 
1784
    CheckComArgOutPointerValid(aValEbx);
 
1785
    CheckComArgOutPointerValid(aValEcx);
 
1786
    CheckComArgOutPointerValid(aValEdx);
 
1787
 
 
1788
    AutoCaller autoCaller(this);
 
1789
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1790
 
 
1791
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1792
 
 
1793
    switch(aId)
 
1794
    {
 
1795
        case 0x0:
 
1796
        case 0x1:
 
1797
        case 0x2:
 
1798
        case 0x3:
 
1799
        case 0x4:
 
1800
        case 0x5:
 
1801
        case 0x6:
 
1802
        case 0x7:
 
1803
        case 0x8:
 
1804
        case 0x9:
 
1805
        case 0xA:
 
1806
            if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
 
1807
                return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
 
1808
 
 
1809
            *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
 
1810
            *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
 
1811
            *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
 
1812
            *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
 
1813
            break;
 
1814
 
 
1815
        case 0x80000000:
 
1816
        case 0x80000001:
 
1817
        case 0x80000002:
 
1818
        case 0x80000003:
 
1819
        case 0x80000004:
 
1820
        case 0x80000005:
 
1821
        case 0x80000006:
 
1822
        case 0x80000007:
 
1823
        case 0x80000008:
 
1824
        case 0x80000009:
 
1825
        case 0x8000000A:
 
1826
            if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
 
1827
                return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
 
1828
 
 
1829
            *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
 
1830
            *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
 
1831
            *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
 
1832
            *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
 
1833
            break;
 
1834
 
 
1835
        default:
 
1836
            return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
 
1837
    }
 
1838
    return S_OK;
 
1839
}
 
1840
 
 
1841
STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
 
1842
{
 
1843
    AutoCaller autoCaller(this);
 
1844
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1845
 
 
1846
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1847
 
 
1848
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1849
    if (FAILED(rc)) return rc;
 
1850
 
 
1851
    switch(aId)
 
1852
    {
 
1853
        case 0x0:
 
1854
        case 0x1:
 
1855
        case 0x2:
 
1856
        case 0x3:
 
1857
        case 0x4:
 
1858
        case 0x5:
 
1859
        case 0x6:
 
1860
        case 0x7:
 
1861
        case 0x8:
 
1862
        case 0x9:
 
1863
        case 0xA:
 
1864
            AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
 
1865
            AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
 
1866
            setModified(IsModified_MachineData);
 
1867
            mHWData.backup();
 
1868
            mHWData->mCpuIdStdLeafs[aId].ulId  = aId;
 
1869
            mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
 
1870
            mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
 
1871
            mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
 
1872
            mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
 
1873
            break;
 
1874
 
 
1875
        case 0x80000000:
 
1876
        case 0x80000001:
 
1877
        case 0x80000002:
 
1878
        case 0x80000003:
 
1879
        case 0x80000004:
 
1880
        case 0x80000005:
 
1881
        case 0x80000006:
 
1882
        case 0x80000007:
 
1883
        case 0x80000008:
 
1884
        case 0x80000009:
 
1885
        case 0x8000000A:
 
1886
            AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
 
1887
            AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
 
1888
            setModified(IsModified_MachineData);
 
1889
            mHWData.backup();
 
1890
            mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId  = aId;
 
1891
            mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
 
1892
            mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
 
1893
            mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
 
1894
            mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
 
1895
            break;
 
1896
 
 
1897
        default:
 
1898
            return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
 
1899
    }
 
1900
    return S_OK;
 
1901
}
 
1902
 
 
1903
STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
 
1904
{
 
1905
    AutoCaller autoCaller(this);
 
1906
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1907
 
 
1908
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1909
 
 
1910
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1911
    if (FAILED(rc)) return rc;
 
1912
 
 
1913
    switch(aId)
 
1914
    {
 
1915
        case 0x0:
 
1916
        case 0x1:
 
1917
        case 0x2:
 
1918
        case 0x3:
 
1919
        case 0x4:
 
1920
        case 0x5:
 
1921
        case 0x6:
 
1922
        case 0x7:
 
1923
        case 0x8:
 
1924
        case 0x9:
 
1925
        case 0xA:
 
1926
            AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
 
1927
            AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
 
1928
            setModified(IsModified_MachineData);
 
1929
            mHWData.backup();
 
1930
            /* Invalidate leaf. */
 
1931
            mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
 
1932
            break;
 
1933
 
 
1934
        case 0x80000000:
 
1935
        case 0x80000001:
 
1936
        case 0x80000002:
 
1937
        case 0x80000003:
 
1938
        case 0x80000004:
 
1939
        case 0x80000005:
 
1940
        case 0x80000006:
 
1941
        case 0x80000007:
 
1942
        case 0x80000008:
 
1943
        case 0x80000009:
 
1944
        case 0x8000000A:
 
1945
            AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
 
1946
            AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
 
1947
            setModified(IsModified_MachineData);
 
1948
            mHWData.backup();
 
1949
            /* Invalidate leaf. */
 
1950
            mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
 
1951
            break;
 
1952
 
 
1953
        default:
 
1954
            return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
 
1955
    }
 
1956
    return S_OK;
 
1957
}
 
1958
 
 
1959
STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
 
1960
{
 
1961
    AutoCaller autoCaller(this);
 
1962
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1963
 
 
1964
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1965
 
 
1966
    HRESULT rc = checkStateDependency(MutableStateDep);
 
1967
    if (FAILED(rc)) return rc;
 
1968
 
 
1969
    setModified(IsModified_MachineData);
 
1970
    mHWData.backup();
 
1971
 
 
1972
    /* Invalidate all standard leafs. */
 
1973
    for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
 
1974
        mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
 
1975
 
 
1976
    /* Invalidate all extended leafs. */
 
1977
    for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
 
1978
        mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
 
1979
 
 
1980
    return S_OK;
 
1981
}
 
1982
 
 
1983
STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
 
1984
{
 
1985
    if (!aVal)
 
1986
        return E_POINTER;
 
1987
 
 
1988
    AutoCaller autoCaller(this);
 
1989
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1990
 
 
1991
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1992
 
 
1993
    switch(property)
 
1994
    {
 
1995
        case HWVirtExPropertyType_Enabled:
 
1996
            *aVal = mHWData->mHWVirtExEnabled;
 
1997
            break;
 
1998
 
 
1999
        case HWVirtExPropertyType_Exclusive:
 
2000
            *aVal = mHWData->mHWVirtExExclusive;
 
2001
            break;
 
2002
 
 
2003
        case HWVirtExPropertyType_VPID:
 
2004
            *aVal = mHWData->mHWVirtExVPIDEnabled;
 
2005
            break;
 
2006
 
 
2007
        case HWVirtExPropertyType_NestedPaging:
 
2008
            *aVal = mHWData->mHWVirtExNestedPagingEnabled;
 
2009
            break;
 
2010
 
 
2011
        case HWVirtExPropertyType_LargePages:
 
2012
            *aVal = mHWData->mHWVirtExLargePagesEnabled;
 
2013
            break;
 
2014
 
 
2015
        case HWVirtExPropertyType_Force:
 
2016
            *aVal = mHWData->mHWVirtExForceEnabled;
 
2017
            break;
 
2018
 
 
2019
        default:
 
2020
            return E_INVALIDARG;
 
2021
    }
 
2022
    return S_OK;
 
2023
}
 
2024
 
 
2025
STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
 
2026
{
 
2027
    AutoCaller autoCaller(this);
 
2028
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2029
 
 
2030
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2031
 
 
2032
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2033
    if (FAILED(rc)) return rc;
 
2034
 
 
2035
    switch(property)
 
2036
    {
 
2037
        case HWVirtExPropertyType_Enabled:
 
2038
            setModified(IsModified_MachineData);
 
2039
            mHWData.backup();
 
2040
            mHWData->mHWVirtExEnabled = !!aVal;
 
2041
            break;
 
2042
 
 
2043
        case HWVirtExPropertyType_Exclusive:
 
2044
            setModified(IsModified_MachineData);
 
2045
            mHWData.backup();
 
2046
            mHWData->mHWVirtExExclusive = !!aVal;
 
2047
            break;
 
2048
 
 
2049
        case HWVirtExPropertyType_VPID:
 
2050
            setModified(IsModified_MachineData);
 
2051
            mHWData.backup();
 
2052
            mHWData->mHWVirtExVPIDEnabled = !!aVal;
 
2053
            break;
 
2054
 
 
2055
        case HWVirtExPropertyType_NestedPaging:
 
2056
            setModified(IsModified_MachineData);
 
2057
            mHWData.backup();
 
2058
            mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
 
2059
            break;
 
2060
 
 
2061
        case HWVirtExPropertyType_LargePages:
 
2062
            setModified(IsModified_MachineData);
 
2063
            mHWData.backup();
 
2064
            mHWData->mHWVirtExLargePagesEnabled = !!aVal;
 
2065
            break;
 
2066
 
 
2067
        case HWVirtExPropertyType_Force:
 
2068
            setModified(IsModified_MachineData);
 
2069
            mHWData.backup();
 
2070
            mHWData->mHWVirtExForceEnabled = !!aVal;
 
2071
            break;
 
2072
 
 
2073
        default:
 
2074
            return E_INVALIDARG;
 
2075
    }
 
2076
 
 
2077
    return S_OK;
 
2078
}
 
2079
 
 
2080
STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
 
2081
{
 
2082
    CheckComArgOutPointerValid(aSnapshotFolder);
 
2083
 
 
2084
    AutoCaller autoCaller(this);
 
2085
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2086
 
 
2087
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2088
 
 
2089
    Utf8Str strFullSnapshotFolder;
 
2090
    calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
 
2091
    strFullSnapshotFolder.cloneTo(aSnapshotFolder);
 
2092
 
 
2093
    return S_OK;
 
2094
}
 
2095
 
 
2096
STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
 
2097
{
 
2098
    /* @todo (r=dmik):
 
2099
     *  1. Allow to change the name of the snapshot folder containing snapshots
 
2100
     *  2. Rename the folder on disk instead of just changing the property
 
2101
     *     value (to be smart and not to leave garbage). Note that it cannot be
 
2102
     *     done here because the change may be rolled back. Thus, the right
 
2103
     *     place is #saveSettings().
 
2104
     */
 
2105
 
 
2106
    AutoCaller autoCaller(this);
 
2107
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2108
 
 
2109
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2110
 
 
2111
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2112
    if (FAILED(rc)) return rc;
 
2113
 
 
2114
    if (!mData->mCurrentSnapshot.isNull())
 
2115
        return setError(E_FAIL,
 
2116
                        tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
 
2117
 
 
2118
    Utf8Str strSnapshotFolder0(aSnapshotFolder);       // keep original
 
2119
 
 
2120
    Utf8Str strSnapshotFolder(strSnapshotFolder0);
 
2121
    if (strSnapshotFolder.isEmpty())
 
2122
        strSnapshotFolder = "Snapshots";
 
2123
    int vrc = calculateFullPath(strSnapshotFolder,
 
2124
                                strSnapshotFolder);
 
2125
    if (RT_FAILURE(vrc))
 
2126
        return setError(E_FAIL,
 
2127
                        tr("Invalid snapshot folder '%ls' (%Rrc)"),
 
2128
                        aSnapshotFolder, vrc);
 
2129
 
 
2130
    setModified(IsModified_MachineData);
 
2131
    mUserData.backup();
 
2132
 
 
2133
    copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
 
2134
 
 
2135
    return S_OK;
 
2136
}
 
2137
 
 
2138
STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
 
2139
{
 
2140
    if (ComSafeArrayOutIsNull(aAttachments))
 
2141
        return E_POINTER;
 
2142
 
 
2143
    AutoCaller autoCaller(this);
 
2144
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2145
 
 
2146
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2147
 
 
2148
    SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
 
2149
    attachments.detachTo(ComSafeArrayOutArg(aAttachments));
 
2150
 
 
2151
    return S_OK;
 
2152
}
 
2153
 
 
2154
STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
 
2155
{
 
2156
    if (!vrdeServer)
 
2157
        return E_POINTER;
 
2158
 
 
2159
    AutoCaller autoCaller(this);
 
2160
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2161
 
 
2162
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2163
 
 
2164
    Assert(!!mVRDEServer);
 
2165
    mVRDEServer.queryInterfaceTo(vrdeServer);
 
2166
 
 
2167
    return S_OK;
 
2168
}
 
2169
 
 
2170
STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
 
2171
{
 
2172
    if (!audioAdapter)
 
2173
        return E_POINTER;
 
2174
 
 
2175
    AutoCaller autoCaller(this);
 
2176
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2177
 
 
2178
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2179
 
 
2180
    mAudioAdapter.queryInterfaceTo(audioAdapter);
 
2181
    return S_OK;
 
2182
}
 
2183
 
 
2184
STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
 
2185
{
 
2186
#ifdef VBOX_WITH_VUSB
 
2187
    CheckComArgOutPointerValid(aUSBController);
 
2188
 
 
2189
    AutoCaller autoCaller(this);
 
2190
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2191
    MultiResult rc(S_OK);
 
2192
 
 
2193
# ifdef VBOX_WITH_USB
 
2194
    rc = mParent->host()->checkUSBProxyService();
 
2195
    if (FAILED(rc)) return rc;
 
2196
# endif
 
2197
 
 
2198
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2199
 
 
2200
    return rc = mUSBController.queryInterfaceTo(aUSBController);
 
2201
#else
 
2202
    /* Note: The GUI depends on this method returning E_NOTIMPL with no
 
2203
     * extended error info to indicate that USB is simply not available
 
2204
     * (w/o treating it as a failure), for example, as in OSE */
 
2205
    NOREF(aUSBController);
 
2206
    ReturnComNotImplemented();
 
2207
#endif /* VBOX_WITH_VUSB */
 
2208
}
 
2209
 
 
2210
STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
 
2211
{
 
2212
    CheckComArgOutPointerValid(aFilePath);
 
2213
 
 
2214
    AutoLimitedCaller autoCaller(this);
 
2215
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2216
 
 
2217
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2218
 
 
2219
    mData->m_strConfigFileFull.cloneTo(aFilePath);
 
2220
    return S_OK;
 
2221
}
 
2222
 
 
2223
STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
 
2224
{
 
2225
    CheckComArgOutPointerValid(aModified);
 
2226
 
 
2227
    AutoCaller autoCaller(this);
 
2228
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2229
 
 
2230
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2231
 
 
2232
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2233
    if (FAILED(rc)) return rc;
 
2234
 
 
2235
    if (!mData->pMachineConfigFile->fileExists())
 
2236
        // this is a new machine, and no config file exists yet:
 
2237
        *aModified = TRUE;
 
2238
    else
 
2239
        *aModified = (mData->flModifications != 0);
 
2240
 
 
2241
    return S_OK;
 
2242
}
 
2243
 
 
2244
STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
 
2245
{
 
2246
    CheckComArgOutPointerValid(aSessionState);
 
2247
 
 
2248
    AutoCaller autoCaller(this);
 
2249
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2250
 
 
2251
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2252
 
 
2253
    *aSessionState = mData->mSession.mState;
 
2254
 
 
2255
    return S_OK;
 
2256
}
 
2257
 
 
2258
STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
 
2259
{
 
2260
    CheckComArgOutPointerValid(aSessionType);
 
2261
 
 
2262
    AutoCaller autoCaller(this);
 
2263
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2264
 
 
2265
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2266
 
 
2267
    mData->mSession.mType.cloneTo(aSessionType);
 
2268
 
 
2269
    return S_OK;
 
2270
}
 
2271
 
 
2272
STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
 
2273
{
 
2274
    CheckComArgOutPointerValid(aSessionPid);
 
2275
 
 
2276
    AutoCaller autoCaller(this);
 
2277
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2278
 
 
2279
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2280
 
 
2281
    *aSessionPid = mData->mSession.mPid;
 
2282
 
 
2283
    return S_OK;
 
2284
}
 
2285
 
 
2286
STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
 
2287
{
 
2288
    if (!machineState)
 
2289
        return E_POINTER;
 
2290
 
 
2291
    AutoCaller autoCaller(this);
 
2292
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2293
 
 
2294
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2295
 
 
2296
    *machineState = mData->mMachineState;
 
2297
 
 
2298
    return S_OK;
 
2299
}
 
2300
 
 
2301
STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
 
2302
{
 
2303
    CheckComArgOutPointerValid(aLastStateChange);
 
2304
 
 
2305
    AutoCaller autoCaller(this);
 
2306
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2307
 
 
2308
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2309
 
 
2310
    *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
 
2311
 
 
2312
    return S_OK;
 
2313
}
 
2314
 
 
2315
STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
 
2316
{
 
2317
    CheckComArgOutPointerValid(aStateFilePath);
 
2318
 
 
2319
    AutoCaller autoCaller(this);
 
2320
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2321
 
 
2322
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2323
 
 
2324
    mSSData->mStateFilePath.cloneTo(aStateFilePath);
 
2325
 
 
2326
    return S_OK;
 
2327
}
 
2328
 
 
2329
STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
 
2330
{
 
2331
    CheckComArgOutPointerValid(aLogFolder);
 
2332
 
 
2333
    AutoCaller autoCaller(this);
 
2334
    AssertComRCReturnRC(autoCaller.rc());
 
2335
 
 
2336
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2337
 
 
2338
    Utf8Str logFolder;
 
2339
    getLogFolder(logFolder);
 
2340
    logFolder.cloneTo(aLogFolder);
 
2341
 
 
2342
    return S_OK;
 
2343
}
 
2344
 
 
2345
STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
 
2346
{
 
2347
    CheckComArgOutPointerValid(aCurrentSnapshot);
 
2348
 
 
2349
    AutoCaller autoCaller(this);
 
2350
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2351
 
 
2352
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2353
 
 
2354
    mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
 
2355
 
 
2356
    return S_OK;
 
2357
}
 
2358
 
 
2359
STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
 
2360
{
 
2361
    CheckComArgOutPointerValid(aSnapshotCount);
 
2362
 
 
2363
    AutoCaller autoCaller(this);
 
2364
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2365
 
 
2366
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2367
 
 
2368
    *aSnapshotCount = mData->mFirstSnapshot.isNull()
 
2369
                          ? 0
 
2370
                          : mData->mFirstSnapshot->getAllChildrenCount() + 1;
 
2371
 
 
2372
    return S_OK;
 
2373
}
 
2374
 
 
2375
STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
 
2376
{
 
2377
    CheckComArgOutPointerValid(aCurrentStateModified);
 
2378
 
 
2379
    AutoCaller autoCaller(this);
 
2380
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2381
 
 
2382
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2383
 
 
2384
    /* Note: for machines with no snapshots, we always return FALSE
 
2385
     * (mData->mCurrentStateModified will be TRUE in this case, for historical
 
2386
     * reasons :) */
 
2387
 
 
2388
    *aCurrentStateModified = mData->mFirstSnapshot.isNull()
 
2389
                            ? FALSE
 
2390
                            : mData->mCurrentStateModified;
 
2391
 
 
2392
    return S_OK;
 
2393
}
 
2394
 
 
2395
STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
 
2396
{
 
2397
    CheckComArgOutSafeArrayPointerValid(aSharedFolders);
 
2398
 
 
2399
    AutoCaller autoCaller(this);
 
2400
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2401
 
 
2402
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2403
 
 
2404
    SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
 
2405
    folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
 
2406
 
 
2407
    return S_OK;
 
2408
}
 
2409
 
 
2410
STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
 
2411
{
 
2412
    CheckComArgOutPointerValid(aClipboardMode);
 
2413
 
 
2414
    AutoCaller autoCaller(this);
 
2415
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2416
 
 
2417
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2418
 
 
2419
    *aClipboardMode = mHWData->mClipboardMode;
 
2420
 
 
2421
    return S_OK;
 
2422
}
 
2423
 
 
2424
STDMETHODIMP
 
2425
Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
 
2426
{
 
2427
    AutoCaller autoCaller(this);
 
2428
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2429
 
 
2430
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2431
 
 
2432
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2433
    if (FAILED(rc)) return rc;
 
2434
 
 
2435
    setModified(IsModified_MachineData);
 
2436
    mHWData.backup();
 
2437
    mHWData->mClipboardMode = aClipboardMode;
 
2438
 
 
2439
    return S_OK;
 
2440
}
 
2441
 
 
2442
STDMETHODIMP
 
2443
Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
 
2444
{
 
2445
    CheckComArgOutPointerValid(aPatterns);
 
2446
 
 
2447
    AutoCaller autoCaller(this);
 
2448
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2449
 
 
2450
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2451
 
 
2452
    try
 
2453
    {
 
2454
        mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
 
2455
    }
 
2456
    catch (...)
 
2457
    {
 
2458
        return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
 
2459
    }
 
2460
 
 
2461
    return S_OK;
 
2462
}
 
2463
 
 
2464
STDMETHODIMP
 
2465
Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
 
2466
{
 
2467
    AutoCaller autoCaller(this);
 
2468
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2469
 
 
2470
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2471
 
 
2472
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2473
    if (FAILED(rc)) return rc;
 
2474
 
 
2475
    setModified(IsModified_MachineData);
 
2476
    mHWData.backup();
 
2477
    mHWData->mGuestPropertyNotificationPatterns = aPatterns;
 
2478
    return rc;
 
2479
}
 
2480
 
 
2481
STDMETHODIMP
 
2482
Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
 
2483
{
 
2484
    CheckComArgOutSafeArrayPointerValid(aStorageControllers);
 
2485
 
 
2486
    AutoCaller autoCaller(this);
 
2487
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2488
 
 
2489
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2490
 
 
2491
    SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
 
2492
    ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
 
2493
 
 
2494
    return S_OK;
 
2495
}
 
2496
 
 
2497
STDMETHODIMP
 
2498
Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
 
2499
{
 
2500
    CheckComArgOutPointerValid(aEnabled);
 
2501
 
 
2502
    AutoCaller autoCaller(this);
 
2503
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2504
 
 
2505
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2506
 
 
2507
    *aEnabled = mUserData->s.fTeleporterEnabled;
 
2508
 
 
2509
    return S_OK;
 
2510
}
 
2511
 
 
2512
STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
 
2513
{
 
2514
    AutoCaller autoCaller(this);
 
2515
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2516
 
 
2517
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2518
 
 
2519
    /* Only allow it to be set to true when PoweredOff or Aborted.
 
2520
       (Clearing it is always permitted.) */
 
2521
    if (    aEnabled
 
2522
        &&  mData->mRegistered
 
2523
        &&  (   !isSessionMachine()
 
2524
             || (   mData->mMachineState != MachineState_PoweredOff
 
2525
                 && mData->mMachineState != MachineState_Teleported
 
2526
                 && mData->mMachineState != MachineState_Aborted
 
2527
                )
 
2528
            )
 
2529
       )
 
2530
        return setError(VBOX_E_INVALID_VM_STATE,
 
2531
                        tr("The machine is not powered off (state is %s)"),
 
2532
                        Global::stringifyMachineState(mData->mMachineState));
 
2533
 
 
2534
    setModified(IsModified_MachineData);
 
2535
    mUserData.backup();
 
2536
    mUserData->s.fTeleporterEnabled = !!aEnabled;
 
2537
 
 
2538
    return S_OK;
 
2539
}
 
2540
 
 
2541
STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
 
2542
{
 
2543
    CheckComArgOutPointerValid(aPort);
 
2544
 
 
2545
    AutoCaller autoCaller(this);
 
2546
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2547
 
 
2548
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2549
 
 
2550
    *aPort = (ULONG)mUserData->s.uTeleporterPort;
 
2551
 
 
2552
    return S_OK;
 
2553
}
 
2554
 
 
2555
STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
 
2556
{
 
2557
    if (aPort >= _64K)
 
2558
        return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
 
2559
 
 
2560
    AutoCaller autoCaller(this);
 
2561
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2562
 
 
2563
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2564
 
 
2565
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2566
    if (FAILED(rc)) return rc;
 
2567
 
 
2568
    setModified(IsModified_MachineData);
 
2569
    mUserData.backup();
 
2570
    mUserData->s.uTeleporterPort = (uint32_t)aPort;
 
2571
 
 
2572
    return S_OK;
 
2573
}
 
2574
 
 
2575
STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
 
2576
{
 
2577
    CheckComArgOutPointerValid(aAddress);
 
2578
 
 
2579
    AutoCaller autoCaller(this);
 
2580
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2581
 
 
2582
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2583
 
 
2584
    mUserData->s.strTeleporterAddress.cloneTo(aAddress);
 
2585
 
 
2586
    return S_OK;
 
2587
}
 
2588
 
 
2589
STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
 
2590
{
 
2591
    AutoCaller autoCaller(this);
 
2592
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2593
 
 
2594
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2595
 
 
2596
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2597
    if (FAILED(rc)) return rc;
 
2598
 
 
2599
    setModified(IsModified_MachineData);
 
2600
    mUserData.backup();
 
2601
    mUserData->s.strTeleporterAddress = aAddress;
 
2602
 
 
2603
    return S_OK;
 
2604
}
 
2605
 
 
2606
STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
 
2607
{
 
2608
    CheckComArgOutPointerValid(aPassword);
 
2609
 
 
2610
    AutoCaller autoCaller(this);
 
2611
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2612
 
 
2613
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2614
 
 
2615
    mUserData->s.strTeleporterPassword.cloneTo(aPassword);
 
2616
 
 
2617
    return S_OK;
 
2618
}
 
2619
 
 
2620
STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
 
2621
{
 
2622
    AutoCaller autoCaller(this);
 
2623
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2624
 
 
2625
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2626
 
 
2627
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2628
    if (FAILED(rc)) return rc;
 
2629
 
 
2630
    setModified(IsModified_MachineData);
 
2631
    mUserData.backup();
 
2632
    mUserData->s.strTeleporterPassword = aPassword;
 
2633
 
 
2634
    return S_OK;
 
2635
}
 
2636
 
 
2637
STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
 
2638
{
 
2639
    CheckComArgOutPointerValid(aState);
 
2640
 
 
2641
    AutoCaller autoCaller(this);
 
2642
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2643
 
 
2644
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2645
 
 
2646
    *aState = mUserData->s.enmFaultToleranceState;
 
2647
    return S_OK;
 
2648
}
 
2649
 
 
2650
STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
 
2651
{
 
2652
    AutoCaller autoCaller(this);
 
2653
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2654
 
 
2655
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2656
 
 
2657
    /* @todo deal with running state change. */
 
2658
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2659
    if (FAILED(rc)) return rc;
 
2660
 
 
2661
    setModified(IsModified_MachineData);
 
2662
    mUserData.backup();
 
2663
    mUserData->s.enmFaultToleranceState = aState;
 
2664
    return S_OK;
 
2665
}
 
2666
 
 
2667
STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
 
2668
{
 
2669
    CheckComArgOutPointerValid(aAddress);
 
2670
 
 
2671
    AutoCaller autoCaller(this);
 
2672
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2673
 
 
2674
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2675
 
 
2676
    mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
 
2677
    return S_OK;
 
2678
}
 
2679
 
 
2680
STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
 
2681
{
 
2682
    AutoCaller autoCaller(this);
 
2683
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2684
 
 
2685
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2686
 
 
2687
    /* @todo deal with running state change. */
 
2688
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2689
    if (FAILED(rc)) return rc;
 
2690
 
 
2691
    setModified(IsModified_MachineData);
 
2692
    mUserData.backup();
 
2693
    mUserData->s.strFaultToleranceAddress = aAddress;
 
2694
    return S_OK;
 
2695
}
 
2696
 
 
2697
STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
 
2698
{
 
2699
    CheckComArgOutPointerValid(aPort);
 
2700
 
 
2701
    AutoCaller autoCaller(this);
 
2702
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2703
 
 
2704
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2705
 
 
2706
    *aPort = mUserData->s.uFaultTolerancePort;
 
2707
    return S_OK;
 
2708
}
 
2709
 
 
2710
STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
 
2711
{
 
2712
    AutoCaller autoCaller(this);
 
2713
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2714
 
 
2715
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2716
 
 
2717
    /* @todo deal with running state change. */
 
2718
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2719
    if (FAILED(rc)) return rc;
 
2720
 
 
2721
    setModified(IsModified_MachineData);
 
2722
    mUserData.backup();
 
2723
    mUserData->s.uFaultTolerancePort = aPort;
 
2724
    return S_OK;
 
2725
}
 
2726
 
 
2727
STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
 
2728
{
 
2729
    CheckComArgOutPointerValid(aPassword);
 
2730
 
 
2731
    AutoCaller autoCaller(this);
 
2732
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2733
 
 
2734
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2735
 
 
2736
    mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
 
2737
 
 
2738
    return S_OK;
 
2739
}
 
2740
 
 
2741
STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
 
2742
{
 
2743
    AutoCaller autoCaller(this);
 
2744
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2745
 
 
2746
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2747
 
 
2748
    /* @todo deal with running state change. */
 
2749
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2750
    if (FAILED(rc)) return rc;
 
2751
 
 
2752
    setModified(IsModified_MachineData);
 
2753
    mUserData.backup();
 
2754
    mUserData->s.strFaultTolerancePassword = aPassword;
 
2755
 
 
2756
    return S_OK;
 
2757
}
 
2758
 
 
2759
STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
 
2760
{
 
2761
    CheckComArgOutPointerValid(aInterval);
 
2762
 
 
2763
    AutoCaller autoCaller(this);
 
2764
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2765
 
 
2766
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2767
 
 
2768
    *aInterval = mUserData->s.uFaultToleranceInterval;
 
2769
    return S_OK;
 
2770
}
 
2771
 
 
2772
STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
 
2773
{
 
2774
    AutoCaller autoCaller(this);
 
2775
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2776
 
 
2777
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2778
 
 
2779
    /* @todo deal with running state change. */
 
2780
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2781
    if (FAILED(rc)) return rc;
 
2782
 
 
2783
    setModified(IsModified_MachineData);
 
2784
    mUserData.backup();
 
2785
    mUserData->s.uFaultToleranceInterval = aInterval;
 
2786
    return S_OK;
 
2787
}
 
2788
 
 
2789
STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
 
2790
{
 
2791
    CheckComArgOutPointerValid(aEnabled);
 
2792
 
 
2793
    AutoCaller autoCaller(this);
 
2794
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2795
 
 
2796
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2797
 
 
2798
    *aEnabled = mUserData->s.fRTCUseUTC;
 
2799
 
 
2800
    return S_OK;
 
2801
}
 
2802
 
 
2803
STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
 
2804
{
 
2805
    AutoCaller autoCaller(this);
 
2806
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2807
 
 
2808
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2809
 
 
2810
    /* Only allow it to be set to true when PoweredOff or Aborted.
 
2811
       (Clearing it is always permitted.) */
 
2812
    if (    aEnabled
 
2813
        &&  mData->mRegistered
 
2814
        &&  (   !isSessionMachine()
 
2815
             || (   mData->mMachineState != MachineState_PoweredOff
 
2816
                 && mData->mMachineState != MachineState_Teleported
 
2817
                 && mData->mMachineState != MachineState_Aborted
 
2818
                )
 
2819
            )
 
2820
       )
 
2821
        return setError(VBOX_E_INVALID_VM_STATE,
 
2822
                        tr("The machine is not powered off (state is %s)"),
 
2823
                        Global::stringifyMachineState(mData->mMachineState));
 
2824
 
 
2825
    setModified(IsModified_MachineData);
 
2826
    mUserData.backup();
 
2827
    mUserData->s.fRTCUseUTC = !!aEnabled;
 
2828
 
 
2829
    return S_OK;
 
2830
}
 
2831
 
 
2832
STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
 
2833
{
 
2834
    CheckComArgOutPointerValid(aEnabled);
 
2835
 
 
2836
    AutoCaller autoCaller(this);
 
2837
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2838
 
 
2839
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2840
 
 
2841
    *aEnabled = mHWData->mIoCacheEnabled;
 
2842
 
 
2843
    return S_OK;
 
2844
}
 
2845
 
 
2846
STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
 
2847
{
 
2848
    AutoCaller autoCaller(this);
 
2849
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2850
 
 
2851
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2852
 
 
2853
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2854
    if (FAILED(rc)) return rc;
 
2855
 
 
2856
    setModified(IsModified_MachineData);
 
2857
    mHWData.backup();
 
2858
    mHWData->mIoCacheEnabled = aEnabled;
 
2859
 
 
2860
    return S_OK;
 
2861
}
 
2862
 
 
2863
STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
 
2864
{
 
2865
    CheckComArgOutPointerValid(aIoCacheSize);
 
2866
 
 
2867
    AutoCaller autoCaller(this);
 
2868
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2869
 
 
2870
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2871
 
 
2872
    *aIoCacheSize = mHWData->mIoCacheSize;
 
2873
 
 
2874
    return S_OK;
 
2875
}
 
2876
 
 
2877
STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG  aIoCacheSize)
 
2878
{
 
2879
    AutoCaller autoCaller(this);
 
2880
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2881
 
 
2882
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2883
 
 
2884
    HRESULT rc = checkStateDependency(MutableStateDep);
 
2885
    if (FAILED(rc)) return rc;
 
2886
 
 
2887
    setModified(IsModified_MachineData);
 
2888
    mHWData.backup();
 
2889
    mHWData->mIoCacheSize = aIoCacheSize;
 
2890
 
 
2891
    return S_OK;
 
2892
}
 
2893
 
 
2894
 
 
2895
/**
 
2896
 *  @note Locks objects!
 
2897
 */
 
2898
STDMETHODIMP Machine::LockMachine(ISession *aSession,
 
2899
                                  LockType_T lockType)
 
2900
{
 
2901
    CheckComArgNotNull(aSession);
 
2902
 
 
2903
    AutoCaller autoCaller(this);
 
2904
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2905
 
 
2906
    /* check the session state */
 
2907
    SessionState_T state;
 
2908
    HRESULT rc = aSession->COMGETTER(State)(&state);
 
2909
    if (FAILED(rc)) return rc;
 
2910
 
 
2911
    if (state != SessionState_Unlocked)
 
2912
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
2913
                        tr("The given session is busy"));
 
2914
 
 
2915
    // get the client's IInternalSessionControl interface
 
2916
    ComPtr<IInternalSessionControl> pSessionControl = aSession;
 
2917
    ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
 
2918
                    E_INVALIDARG);
 
2919
 
 
2920
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2921
 
 
2922
    if (!mData->mRegistered)
 
2923
        return setError(E_UNEXPECTED,
 
2924
                        tr("The machine '%s' is not registered"),
 
2925
                        mUserData->s.strName.c_str());
 
2926
 
 
2927
    LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
 
2928
 
 
2929
    SessionState_T oldState = mData->mSession.mState;
 
2930
    /* Hack: in case the session is closing and there is a progress object
 
2931
     * which allows waiting for the session to be closed, take the opportunity
 
2932
     * and do a limited wait (max. 1 second). This helps a lot when the system
 
2933
     * is busy and thus session closing can take a little while. */
 
2934
    if (    mData->mSession.mState == SessionState_Unlocking
 
2935
        &&  mData->mSession.mProgress)
 
2936
    {
 
2937
        alock.release();
 
2938
        mData->mSession.mProgress->WaitForCompletion(1000);
 
2939
        alock.acquire();
 
2940
        LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
 
2941
    }
 
2942
 
 
2943
    // try again now
 
2944
    if (    (mData->mSession.mState == SessionState_Locked)         // machine is write-locked already (i.e. session machine exists)
 
2945
         && (lockType == LockType_Shared)                           // caller wants a shared link to the existing session that holds the write lock:
 
2946
       )
 
2947
    {
 
2948
        // OK, share the session... we are now dealing with three processes:
 
2949
        // 1) VBoxSVC (where this code runs);
 
2950
        // 2) process C: the caller's client process (who wants a shared session);
 
2951
        // 3) process W: the process which already holds the write lock on the machine (write-locking session)
 
2952
 
 
2953
        // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
 
2954
        ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
 
2955
        ComAssertRet(!pSessionW.isNull(), E_FAIL);
 
2956
        ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
 
2957
        AssertReturn(!pSessionMachine.isNull(), E_FAIL);
 
2958
 
 
2959
        /*
 
2960
         *  Leave the lock before calling the client process. It's safe here
 
2961
         *  since the only thing to do after we get the lock again is to add
 
2962
         *  the remote control to the list (which doesn't directly influence
 
2963
         *  anything).
 
2964
         */
 
2965
        alock.leave();
 
2966
 
 
2967
        // get the console of the session holding the write lock (this is a remote call)
 
2968
        ComPtr<IConsole> pConsoleW;
 
2969
        LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
 
2970
        rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
 
2971
        LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
 
2972
        if (FAILED(rc))
 
2973
            // the failure may occur w/o any error info (from RPC), so provide one
 
2974
            return setError(VBOX_E_VM_ERROR,
 
2975
                            tr("Failed to get a console object from the direct session (%Rrc)"), rc);
 
2976
 
 
2977
        ComAssertRet(!pConsoleW.isNull(), E_FAIL);
 
2978
 
 
2979
        // share the session machine and W's console with the caller's session
 
2980
        LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
 
2981
        rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
 
2982
        LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
 
2983
 
 
2984
        if (FAILED(rc))
 
2985
            // the failure may occur w/o any error info (from RPC), so provide one
 
2986
            return setError(VBOX_E_VM_ERROR,
 
2987
                            tr("Failed to assign the machine to the session (%Rrc)"), rc);
 
2988
        alock.enter();
 
2989
 
 
2990
        // need to revalidate the state after entering the lock again
 
2991
        if (mData->mSession.mState != SessionState_Locked)
 
2992
        {
 
2993
            pSessionControl->Uninitialize();
 
2994
            return setError(VBOX_E_INVALID_SESSION_STATE,
 
2995
                            tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
 
2996
                               mUserData->s.strName.c_str());
 
2997
        }
 
2998
 
 
2999
        // add the caller's session to the list
 
3000
        mData->mSession.mRemoteControls.push_back(pSessionControl);
 
3001
    }
 
3002
    else if (    mData->mSession.mState == SessionState_Locked
 
3003
              || mData->mSession.mState == SessionState_Unlocking
 
3004
            )
 
3005
    {
 
3006
        // sharing not permitted, or machine still unlocking:
 
3007
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
3008
                        tr("The machine '%s' is already locked for a session (or being unlocked)"),
 
3009
                        mUserData->s.strName.c_str());
 
3010
    }
 
3011
    else
 
3012
    {
 
3013
        // machine is not locked: then write-lock the machine (create the session machine)
 
3014
 
 
3015
        // must not be busy
 
3016
        AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
 
3017
 
 
3018
        // get the caller's session PID
 
3019
        RTPROCESS pid = NIL_RTPROCESS;
 
3020
        AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
 
3021
        pSessionControl->GetPID((ULONG*)&pid);
 
3022
        Assert(pid != NIL_RTPROCESS);
 
3023
 
 
3024
        bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
 
3025
 
 
3026
        if (fLaunchingVMProcess)
 
3027
        {
 
3028
            // this machine is awaiting for a spawning session to be opened:
 
3029
            // then the calling process must be the one that got started by
 
3030
            // launchVMProcess()
 
3031
 
 
3032
            LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid));
 
3033
            LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
 
3034
 
 
3035
            if (mData->mSession.mPid != pid)
 
3036
                return setError(E_ACCESSDENIED,
 
3037
                                tr("An unexpected process (PID=0x%08X) has tried to lock the "
 
3038
                                   "machine '%s', while only the process started by launchVMProcess (PID=0x%08X) is allowed"),
 
3039
                                pid, mUserData->s.strName.c_str(), mData->mSession.mPid);
 
3040
        }
 
3041
 
 
3042
        // create the mutable SessionMachine from the current machine
 
3043
        ComObjPtr<SessionMachine> sessionMachine;
 
3044
        sessionMachine.createObject();
 
3045
        rc = sessionMachine->init(this);
 
3046
        AssertComRC(rc);
 
3047
 
 
3048
        /* NOTE: doing return from this function after this point but
 
3049
         * before the end is forbidden since it may call SessionMachine::uninit()
 
3050
         * (through the ComObjPtr's destructor) which requests the VirtualBox write
 
3051
         * lock while still holding the Machine lock in alock so that a deadlock
 
3052
         * is possible due to the wrong lock order. */
 
3053
 
 
3054
        if (SUCCEEDED(rc))
 
3055
        {
 
3056
            /*
 
3057
             *  Set the session state to Spawning to protect against subsequent
 
3058
             *  attempts to open a session and to unregister the machine after
 
3059
             *  we leave the lock.
 
3060
             */
 
3061
            SessionState_T origState = mData->mSession.mState;
 
3062
            mData->mSession.mState = SessionState_Spawning;
 
3063
 
 
3064
            /*
 
3065
             *  Leave the lock before calling the client process -- it will call
 
3066
             *  Machine/SessionMachine methods. Leaving the lock here is quite safe
 
3067
             *  because the state is Spawning, so that openRemotesession() and
 
3068
             *  openExistingSession() calls will fail. This method, called before we
 
3069
             *  enter the lock again, will fail because of the wrong PID.
 
3070
             *
 
3071
             *  Note that mData->mSession.mRemoteControls accessed outside
 
3072
             *  the lock may not be modified when state is Spawning, so it's safe.
 
3073
             */
 
3074
            alock.leave();
 
3075
 
 
3076
            LogFlowThisFunc(("Calling AssignMachine()...\n"));
 
3077
            rc = pSessionControl->AssignMachine(sessionMachine);
 
3078
            LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
 
3079
 
 
3080
            /* The failure may occur w/o any error info (from RPC), so provide one */
 
3081
            if (FAILED(rc))
 
3082
                setError(VBOX_E_VM_ERROR,
 
3083
                         tr("Failed to assign the machine to the session (%Rrc)"), rc);
 
3084
 
 
3085
            if (    SUCCEEDED(rc)
 
3086
                 && fLaunchingVMProcess
 
3087
               )
 
3088
            {
 
3089
                /* complete the remote session initialization */
 
3090
 
 
3091
                /* get the console from the direct session */
 
3092
                ComPtr<IConsole> console;
 
3093
                rc = pSessionControl->GetRemoteConsole(console.asOutParam());
 
3094
                ComAssertComRC(rc);
 
3095
 
 
3096
                if (SUCCEEDED(rc) && !console)
 
3097
                {
 
3098
                    ComAssert(!!console);
 
3099
                    rc = E_FAIL;
 
3100
                }
 
3101
 
 
3102
                /* assign machine & console to the remote session */
 
3103
                if (SUCCEEDED(rc))
 
3104
                {
 
3105
                    /*
 
3106
                     *  after openRemoteSession(), the first and the only
 
3107
                     *  entry in remoteControls is that remote session
 
3108
                     */
 
3109
                    LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
 
3110
                    rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
 
3111
                    LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
 
3112
 
 
3113
                    /* The failure may occur w/o any error info (from RPC), so provide one */
 
3114
                    if (FAILED(rc))
 
3115
                        setError(VBOX_E_VM_ERROR,
 
3116
                                 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
 
3117
                }
 
3118
 
 
3119
                if (FAILED(rc))
 
3120
                    pSessionControl->Uninitialize();
 
3121
            }
 
3122
 
 
3123
            /* enter the lock again */
 
3124
            alock.enter();
 
3125
 
 
3126
            /* Restore the session state */
 
3127
            mData->mSession.mState = origState;
 
3128
        }
 
3129
 
 
3130
        // finalize spawning anyway (this is why we don't return on errors above)
 
3131
        if (fLaunchingVMProcess)
 
3132
        {
 
3133
            /* Note that the progress object is finalized later */
 
3134
            /** @todo Consider checking mData->mSession.mProgress for cancellation
 
3135
             *        around here.  */
 
3136
 
 
3137
            /* We don't reset mSession.mPid here because it is necessary for
 
3138
             * SessionMachine::uninit() to reap the child process later. */
 
3139
 
 
3140
            if (FAILED(rc))
 
3141
            {
 
3142
                /* Close the remote session, remove the remote control from the list
 
3143
                 * and reset session state to Closed (@note keep the code in sync
 
3144
                 * with the relevant part in openSession()). */
 
3145
 
 
3146
                Assert(mData->mSession.mRemoteControls.size() == 1);
 
3147
                if (mData->mSession.mRemoteControls.size() == 1)
 
3148
                {
 
3149
                    ErrorInfoKeeper eik;
 
3150
                    mData->mSession.mRemoteControls.front()->Uninitialize();
 
3151
                }
 
3152
 
 
3153
                mData->mSession.mRemoteControls.clear();
 
3154
                mData->mSession.mState = SessionState_Unlocked;
 
3155
            }
 
3156
        }
 
3157
        else
 
3158
        {
 
3159
            /* memorize PID of the directly opened session */
 
3160
            if (SUCCEEDED(rc))
 
3161
                mData->mSession.mPid = pid;
 
3162
        }
 
3163
 
 
3164
        if (SUCCEEDED(rc))
 
3165
        {
 
3166
            /* memorize the direct session control and cache IUnknown for it */
 
3167
            mData->mSession.mDirectControl = pSessionControl;
 
3168
            mData->mSession.mState = SessionState_Locked;
 
3169
            /* associate the SessionMachine with this Machine */
 
3170
            mData->mSession.mMachine = sessionMachine;
 
3171
 
 
3172
            /* request an IUnknown pointer early from the remote party for later
 
3173
             * identity checks (it will be internally cached within mDirectControl
 
3174
             * at least on XPCOM) */
 
3175
            ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
 
3176
            NOREF(unk);
 
3177
        }
 
3178
 
 
3179
        /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
 
3180
         * would break the lock order */
 
3181
        alock.leave();
 
3182
 
 
3183
        /* uninitialize the created session machine on failure */
 
3184
        if (FAILED(rc))
 
3185
            sessionMachine->uninit();
 
3186
 
 
3187
    }
 
3188
 
 
3189
    if (SUCCEEDED(rc))
 
3190
    {
 
3191
        /*
 
3192
        *  tell the client watcher thread to update the set of
 
3193
        *  machines that have open sessions
 
3194
        */
 
3195
        mParent->updateClientWatcher();
 
3196
 
 
3197
        if (oldState != SessionState_Locked)
 
3198
            /* fire an event */
 
3199
            mParent->onSessionStateChange(getId(), SessionState_Locked);
 
3200
    }
 
3201
 
 
3202
    return rc;
 
3203
}
 
3204
 
 
3205
/**
 
3206
 *  @note Locks objects!
 
3207
 */
 
3208
STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
 
3209
                                      IN_BSTR aType,
 
3210
                                      IN_BSTR aEnvironment,
 
3211
                                      IProgress **aProgress)
 
3212
{
 
3213
    CheckComArgNotNull(aSession);
 
3214
    CheckComArgStrNotEmptyOrNull(aType);
 
3215
    CheckComArgOutPointerValid(aProgress);
 
3216
 
 
3217
    AutoCaller autoCaller(this);
 
3218
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3219
 
 
3220
    /* check the session state */
 
3221
    SessionState_T state;
 
3222
    HRESULT rc = aSession->COMGETTER(State)(&state);
 
3223
    if (FAILED(rc)) return rc;
 
3224
 
 
3225
    if (state != SessionState_Unlocked)
 
3226
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
3227
                        tr("The given session is busy"));
 
3228
 
 
3229
    /* get the IInternalSessionControl interface */
 
3230
    ComPtr<IInternalSessionControl> control = aSession;
 
3231
    ComAssertMsgRet(!!control, ("No IInternalSessionControl interface"),
 
3232
                      E_INVALIDARG);
 
3233
 
 
3234
    /* get the teleporter enable state for the progress object init. */
 
3235
    BOOL fTeleporterEnabled;
 
3236
    rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
 
3237
    if (FAILED(rc))
 
3238
        return rc;
 
3239
 
 
3240
    /* create a progress object */
 
3241
    ComObjPtr<ProgressProxy> progress;
 
3242
    progress.createObject();
 
3243
    rc = progress->init(mParent,
 
3244
                        static_cast<IMachine*>(this),
 
3245
                        Bstr(tr("Spawning session")).raw(),
 
3246
                        TRUE /* aCancelable */,
 
3247
                        fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
 
3248
                        Bstr(tr("Spawning session")).raw(),
 
3249
                        2 /* uFirstOperationWeight */,
 
3250
                        fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
 
3251
    if (SUCCEEDED(rc))
 
3252
    {
 
3253
        rc = openRemoteSession(control, aType, aEnvironment, progress);
 
3254
        if (SUCCEEDED(rc))
 
3255
        {
 
3256
            progress.queryInterfaceTo(aProgress);
 
3257
 
 
3258
            /* signal the client watcher thread */
 
3259
            mParent->updateClientWatcher();
 
3260
 
 
3261
            /* fire an event */
 
3262
            mParent->onSessionStateChange(getId(), SessionState_Spawning);
 
3263
        }
 
3264
    }
 
3265
 
 
3266
    return rc;
 
3267
}
 
3268
 
 
3269
STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
 
3270
{
 
3271
    if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
 
3272
        return setError(E_INVALIDARG,
 
3273
                        tr("Invalid boot position: %lu (must be in range [1, %lu])"),
 
3274
                        aPosition, SchemaDefs::MaxBootPosition);
 
3275
 
 
3276
    if (aDevice == DeviceType_USB)
 
3277
        return setError(E_NOTIMPL,
 
3278
                        tr("Booting from USB device is currently not supported"));
 
3279
 
 
3280
    AutoCaller autoCaller(this);
 
3281
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3282
 
 
3283
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3284
 
 
3285
    HRESULT rc = checkStateDependency(MutableStateDep);
 
3286
    if (FAILED(rc)) return rc;
 
3287
 
 
3288
    setModified(IsModified_MachineData);
 
3289
    mHWData.backup();
 
3290
    mHWData->mBootOrder[aPosition - 1] = aDevice;
 
3291
 
 
3292
    return S_OK;
 
3293
}
 
3294
 
 
3295
STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
 
3296
{
 
3297
    if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
 
3298
        return setError(E_INVALIDARG,
 
3299
                       tr("Invalid boot position: %lu (must be in range [1, %lu])"),
 
3300
                       aPosition, SchemaDefs::MaxBootPosition);
 
3301
 
 
3302
    AutoCaller autoCaller(this);
 
3303
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3304
 
 
3305
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3306
 
 
3307
    *aDevice = mHWData->mBootOrder[aPosition - 1];
 
3308
 
 
3309
    return S_OK;
 
3310
}
 
3311
 
 
3312
STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
 
3313
                                   LONG aControllerPort,
 
3314
                                   LONG aDevice,
 
3315
                                   DeviceType_T aType,
 
3316
                                   IMedium *aMedium)
 
3317
{
 
3318
    LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aId=\"%ls\"\n",
 
3319
                     aControllerName, aControllerPort, aDevice, aType, aMedium));
 
3320
 
 
3321
    CheckComArgStrNotEmptyOrNull(aControllerName);
 
3322
 
 
3323
    AutoCaller autoCaller(this);
 
3324
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3325
 
 
3326
    // request the host lock first, since might be calling Host methods for getting host drives;
 
3327
    // next, protect the media tree all the while we're in here, as well as our member variables
 
3328
    AutoMultiWriteLock2 alock(mParent->host()->lockHandle(),
 
3329
                              this->lockHandle() COMMA_LOCKVAL_SRC_POS);
 
3330
    AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
3331
 
 
3332
    HRESULT rc = checkStateDependency(MutableStateDep);
 
3333
    if (FAILED(rc)) return rc;
 
3334
 
 
3335
    GuidList llRegistriesThatNeedSaving;
 
3336
 
 
3337
    /// @todo NEWMEDIA implicit machine registration
 
3338
    if (!mData->mRegistered)
 
3339
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
3340
                        tr("Cannot attach storage devices to an unregistered machine"));
 
3341
 
 
3342
    AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
 
3343
 
 
3344
    if (Global::IsOnlineOrTransient(mData->mMachineState))
 
3345
        return setError(VBOX_E_INVALID_VM_STATE,
 
3346
                        tr("Invalid machine state: %s"),
 
3347
                        Global::stringifyMachineState(mData->mMachineState));
 
3348
 
 
3349
    /* Check for an existing controller. */
 
3350
    ComObjPtr<StorageController> ctl;
 
3351
    rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
 
3352
    if (FAILED(rc)) return rc;
 
3353
 
 
3354
    // check that the port and device are not out of range
 
3355
    rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
 
3356
    if (FAILED(rc)) return rc;
 
3357
 
 
3358
    /* check if the device slot is already busy */
 
3359
    MediumAttachment *pAttachTemp;
 
3360
    if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
 
3361
                                      aControllerName,
 
3362
                                      aControllerPort,
 
3363
                                      aDevice)))
 
3364
    {
 
3365
        Medium *pMedium = pAttachTemp->getMedium();
 
3366
        if (pMedium)
 
3367
        {
 
3368
            AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
 
3369
            return setError(VBOX_E_OBJECT_IN_USE,
 
3370
                            tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
 
3371
                            pMedium->getLocationFull().c_str(),
 
3372
                            aControllerPort,
 
3373
                            aDevice,
 
3374
                            aControllerName);
 
3375
        }
 
3376
        else
 
3377
            return setError(VBOX_E_OBJECT_IN_USE,
 
3378
                            tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
 
3379
                            aControllerPort, aDevice, aControllerName);
 
3380
    }
 
3381
 
 
3382
    ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
 
3383
    if (aMedium && medium.isNull())
 
3384
        return setError(E_INVALIDARG, "The given medium pointer is invalid");
 
3385
 
 
3386
    AutoCaller mediumCaller(medium);
 
3387
    if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
 
3388
 
 
3389
    AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
 
3390
 
 
3391
    if (    (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
 
3392
         && !medium.isNull()
 
3393
       )
 
3394
        return setError(VBOX_E_OBJECT_IN_USE,
 
3395
                        tr("Medium '%s' is already attached to this virtual machine"),
 
3396
                        medium->getLocationFull().c_str());
 
3397
 
 
3398
    if (!medium.isNull())
 
3399
    {
 
3400
        MediumType_T mtype = medium->getType();
 
3401
        // MediumType_Readonly is also new, but only applies to DVDs and floppies.
 
3402
        // For DVDs it's not written to the config file, so needs no global config
 
3403
        // version bump. For floppies it's a new attribute "type", which is ignored
 
3404
        // by older VirtualBox version, so needs no global config version bump either.
 
3405
        // For hard disks this type is not accepted.
 
3406
        if (mtype == MediumType_MultiAttach)
 
3407
        {
 
3408
            // This type is new with VirtualBox 4.0 and therefore requires settings
 
3409
            // version 1.11 in the settings backend. Unfortunately it is not enough to do
 
3410
            // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
 
3411
            // two reasons: The medium type is a property of the media registry tree, which
 
3412
            // can reside in the global config file (for pre-4.0 media); we would therefore
 
3413
            // possibly need to bump the global config version. We don't want to do that though
 
3414
            // because that might make downgrading to pre-4.0 impossible.
 
3415
            // As a result, we can only use these two new types if the medium is NOT in the
 
3416
            // global registry:
 
3417
            const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
 
3418
            if (    medium->isInRegistry(uuidGlobalRegistry)
 
3419
                 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
 
3420
               )
 
3421
                return setError(VBOX_E_INVALID_OBJECT_STATE,
 
3422
                                tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
 
3423
                                   "to machines that were created with VirtualBox 4.0 or later"),
 
3424
                                medium->getLocationFull().c_str());
 
3425
        }
 
3426
    }
 
3427
 
 
3428
    bool fIndirect = false;
 
3429
    if (!medium.isNull())
 
3430
        fIndirect = medium->isReadOnly();
 
3431
    bool associate = true;
 
3432
 
 
3433
    do
 
3434
    {
 
3435
        if (    aType == DeviceType_HardDisk
 
3436
             && mMediaData.isBackedUp())
 
3437
        {
 
3438
            const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
 
3439
 
 
3440
            /* check if the medium was attached to the VM before we started
 
3441
             * changing attachments in which case the attachment just needs to
 
3442
             * be restored */
 
3443
            if ((pAttachTemp = findAttachment(oldAtts, medium)))
 
3444
            {
 
3445
                AssertReturn(!fIndirect, E_FAIL);
 
3446
 
 
3447
                /* see if it's the same bus/channel/device */
 
3448
                if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
 
3449
                {
 
3450
                    /* the simplest case: restore the whole attachment
 
3451
                     * and return, nothing else to do */
 
3452
                    mMediaData->mAttachments.push_back(pAttachTemp);
 
3453
                    return S_OK;
 
3454
                }
 
3455
 
 
3456
                /* bus/channel/device differ; we need a new attachment object,
 
3457
                 * but don't try to associate it again */
 
3458
                associate = false;
 
3459
                break;
 
3460
            }
 
3461
        }
 
3462
 
 
3463
        /* go further only if the attachment is to be indirect */
 
3464
        if (!fIndirect)
 
3465
            break;
 
3466
 
 
3467
        /* perform the so called smart attachment logic for indirect
 
3468
         * attachments. Note that smart attachment is only applicable to base
 
3469
         * hard disks. */
 
3470
 
 
3471
        if (medium->getParent().isNull())
 
3472
        {
 
3473
            /* first, investigate the backup copy of the current hard disk
 
3474
             * attachments to make it possible to re-attach existing diffs to
 
3475
             * another device slot w/o losing their contents */
 
3476
            if (mMediaData.isBackedUp())
 
3477
            {
 
3478
                const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
 
3479
 
 
3480
                MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
 
3481
                uint32_t foundLevel = 0;
 
3482
 
 
3483
                for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
 
3484
                     it != oldAtts.end();
 
3485
                     ++it)
 
3486
                {
 
3487
                    uint32_t level = 0;
 
3488
                    MediumAttachment *pAttach = *it;
 
3489
                    ComObjPtr<Medium> pMedium = pAttach->getMedium();
 
3490
                    Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
 
3491
                    if (pMedium.isNull())
 
3492
                        continue;
 
3493
 
 
3494
                    if (pMedium->getBase(&level) == medium)
 
3495
                    {
 
3496
                        /* skip the hard disk if its currently attached (we
 
3497
                         * cannot attach the same hard disk twice) */
 
3498
                        if (findAttachment(mMediaData->mAttachments,
 
3499
                                           pMedium))
 
3500
                            continue;
 
3501
 
 
3502
                        /* matched device, channel and bus (i.e. attached to the
 
3503
                         * same place) will win and immediately stop the search;
 
3504
                         * otherwise the attachment that has the youngest
 
3505
                         * descendant of medium will be used
 
3506
                         */
 
3507
                        if (pAttach->matches(aControllerName, aControllerPort, aDevice))
 
3508
                        {
 
3509
                            /* the simplest case: restore the whole attachment
 
3510
                             * and return, nothing else to do */
 
3511
                            mMediaData->mAttachments.push_back(*it);
 
3512
                            return S_OK;
 
3513
                        }
 
3514
                        else if (    foundIt == oldAtts.end()
 
3515
                                  || level > foundLevel /* prefer younger */
 
3516
                                )
 
3517
                        {
 
3518
                            foundIt = it;
 
3519
                            foundLevel = level;
 
3520
                        }
 
3521
                    }
 
3522
                }
 
3523
 
 
3524
                if (foundIt != oldAtts.end())
 
3525
                {
 
3526
                    /* use the previously attached hard disk */
 
3527
                    medium = (*foundIt)->getMedium();
 
3528
                    mediumCaller.attach(medium);
 
3529
                    if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
 
3530
                    mediumLock.attach(medium);
 
3531
                    /* not implicit, doesn't require association with this VM */
 
3532
                    fIndirect = false;
 
3533
                    associate = false;
 
3534
                    /* go right to the MediumAttachment creation */
 
3535
                    break;
 
3536
                }
 
3537
            }
 
3538
 
 
3539
            /* must give up the medium lock and medium tree lock as below we
 
3540
             * go over snapshots, which needs a lock with higher lock order. */
 
3541
            mediumLock.release();
 
3542
            treeLock.release();
 
3543
 
 
3544
            /* then, search through snapshots for the best diff in the given
 
3545
             * hard disk's chain to base the new diff on */
 
3546
 
 
3547
            ComObjPtr<Medium> base;
 
3548
            ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
 
3549
            while (snap)
 
3550
            {
 
3551
                AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
 
3552
 
 
3553
                const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
 
3554
 
 
3555
                MediumAttachment *pAttachFound = NULL;
 
3556
                uint32_t foundLevel = 0;
 
3557
 
 
3558
                for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
 
3559
                     it != snapAtts.end();
 
3560
                     ++it)
 
3561
                {
 
3562
                    MediumAttachment *pAttach = *it;
 
3563
                    ComObjPtr<Medium> pMedium = pAttach->getMedium();
 
3564
                    Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
 
3565
                    if (pMedium.isNull())
 
3566
                        continue;
 
3567
 
 
3568
                    uint32_t level = 0;
 
3569
                    if (pMedium->getBase(&level) == medium)
 
3570
                    {
 
3571
                        /* matched device, channel and bus (i.e. attached to the
 
3572
                         * same place) will win and immediately stop the search;
 
3573
                         * otherwise the attachment that has the youngest
 
3574
                         * descendant of medium will be used
 
3575
                         */
 
3576
                        if (    pAttach->getDevice() == aDevice
 
3577
                             && pAttach->getPort() == aControllerPort
 
3578
                             && pAttach->getControllerName() == aControllerName
 
3579
                           )
 
3580
                        {
 
3581
                            pAttachFound = pAttach;
 
3582
                            break;
 
3583
                        }
 
3584
                        else if (    !pAttachFound
 
3585
                                  || level > foundLevel /* prefer younger */
 
3586
                                )
 
3587
                        {
 
3588
                            pAttachFound = pAttach;
 
3589
                            foundLevel = level;
 
3590
                        }
 
3591
                    }
 
3592
                }
 
3593
 
 
3594
                if (pAttachFound)
 
3595
                {
 
3596
                    base = pAttachFound->getMedium();
 
3597
                    break;
 
3598
                }
 
3599
 
 
3600
                snap = snap->getParent();
 
3601
            }
 
3602
 
 
3603
            /* re-lock medium tree and the medium, as we need it below */
 
3604
            treeLock.acquire();
 
3605
            mediumLock.acquire();
 
3606
 
 
3607
            /* found a suitable diff, use it as a base */
 
3608
            if (!base.isNull())
 
3609
            {
 
3610
                medium = base;
 
3611
                mediumCaller.attach(medium);
 
3612
                if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
 
3613
                mediumLock.attach(medium);
 
3614
            }
 
3615
        }
 
3616
 
 
3617
        Utf8Str strFullSnapshotFolder;
 
3618
        calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
 
3619
 
 
3620
        ComObjPtr<Medium> diff;
 
3621
        diff.createObject();
 
3622
        rc = diff->init(mParent,
 
3623
                        medium->getPreferredDiffFormat(),
 
3624
                        strFullSnapshotFolder.append(RTPATH_SLASH_STR),
 
3625
                        medium->getFirstRegistryMachineId(),         // store this diff in the same registry as the parent
 
3626
                        &llRegistriesThatNeedSaving);
 
3627
        if (FAILED(rc)) return rc;
 
3628
 
 
3629
        /* Apply the normal locking logic to the entire chain. */
 
3630
        MediumLockList *pMediumLockList(new MediumLockList());
 
3631
        rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
 
3632
                                        true /* fMediumLockWrite */,
 
3633
                                        medium,
 
3634
                                        *pMediumLockList);
 
3635
        if (SUCCEEDED(rc))
 
3636
        {
 
3637
            rc = pMediumLockList->Lock();
 
3638
            if (FAILED(rc))
 
3639
                setError(rc,
 
3640
                         tr("Could not lock medium when creating diff '%s'"),
 
3641
                         diff->getLocationFull().c_str());
 
3642
            else
 
3643
            {
 
3644
                /* will leave the lock before the potentially lengthy operation, so
 
3645
                 * protect with the special state */
 
3646
                MachineState_T oldState = mData->mMachineState;
 
3647
                setMachineState(MachineState_SettingUp);
 
3648
 
 
3649
                mediumLock.leave();
 
3650
                treeLock.leave();
 
3651
                alock.leave();
 
3652
 
 
3653
                rc = medium->createDiffStorage(diff,
 
3654
                                               MediumVariant_Standard,
 
3655
                                               pMediumLockList,
 
3656
                                               NULL /* aProgress */,
 
3657
                                               true /* aWait */,
 
3658
                                               &llRegistriesThatNeedSaving);
 
3659
 
 
3660
                alock.enter();
 
3661
                treeLock.enter();
 
3662
                mediumLock.enter();
 
3663
 
 
3664
                setMachineState(oldState);
 
3665
            }
 
3666
        }
 
3667
 
 
3668
        /* Unlock the media and free the associated memory. */
 
3669
        delete pMediumLockList;
 
3670
 
 
3671
        if (FAILED(rc)) return rc;
 
3672
 
 
3673
        /* use the created diff for the actual attachment */
 
3674
        medium = diff;
 
3675
        mediumCaller.attach(medium);
 
3676
        if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
 
3677
        mediumLock.attach(medium);
 
3678
    }
 
3679
    while (0);
 
3680
 
 
3681
    ComObjPtr<MediumAttachment> attachment;
 
3682
    attachment.createObject();
 
3683
    rc = attachment->init(this,
 
3684
                          medium,
 
3685
                          aControllerName,
 
3686
                          aControllerPort,
 
3687
                          aDevice,
 
3688
                          aType,
 
3689
                          fIndirect,
 
3690
                          NULL);
 
3691
    if (FAILED(rc)) return rc;
 
3692
 
 
3693
    if (associate && !medium.isNull())
 
3694
    {
 
3695
        // as the last step, associate the medium to the VM
 
3696
        rc = medium->addBackReference(mData->mUuid);
 
3697
        // here we can fail because of Deleting, or being in process of creating a Diff
 
3698
        if (FAILED(rc)) return rc;
 
3699
 
 
3700
        // and decide which medium registry to use now that the medium is attached:
 
3701
        Guid uuid;
 
3702
        if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
 
3703
            // machine XML is VirtualBox 4.0 or higher:
 
3704
            uuid = getId();     // machine UUID
 
3705
        else
 
3706
            uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
 
3707
 
 
3708
        if (medium->addRegistry(uuid))
 
3709
            // registry actually changed:
 
3710
            mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, uuid);
 
3711
    }
 
3712
 
 
3713
    /* success: finally remember the attachment */
 
3714
    setModified(IsModified_Storage);
 
3715
    mMediaData.backup();
 
3716
    mMediaData->mAttachments.push_back(attachment);
 
3717
 
 
3718
    mediumLock.release();
 
3719
    treeLock.leave();
 
3720
    alock.release();
 
3721
 
 
3722
    mParent->saveRegistries(llRegistriesThatNeedSaving);
 
3723
 
 
3724
    return rc;
 
3725
}
 
3726
 
 
3727
STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
 
3728
                                   LONG aDevice)
 
3729
{
 
3730
    CheckComArgStrNotEmptyOrNull(aControllerName);
 
3731
 
 
3732
    LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
 
3733
                     aControllerName, aControllerPort, aDevice));
 
3734
 
 
3735
    AutoCaller autoCaller(this);
 
3736
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3737
 
 
3738
    GuidList llRegistriesThatNeedSaving;
 
3739
 
 
3740
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3741
 
 
3742
    HRESULT rc = checkStateDependency(MutableStateDep);
 
3743
    if (FAILED(rc)) return rc;
 
3744
 
 
3745
    AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
 
3746
 
 
3747
    if (Global::IsOnlineOrTransient(mData->mMachineState))
 
3748
        return setError(VBOX_E_INVALID_VM_STATE,
 
3749
                        tr("Invalid machine state: %s"),
 
3750
                        Global::stringifyMachineState(mData->mMachineState));
 
3751
 
 
3752
    MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
 
3753
                                               aControllerName,
 
3754
                                               aControllerPort,
 
3755
                                               aDevice);
 
3756
    if (!pAttach)
 
3757
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
3758
                        tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
 
3759
                        aDevice, aControllerPort, aControllerName);
 
3760
 
 
3761
    rc = detachDevice(pAttach, alock, NULL /* pSnapshot */, &llRegistriesThatNeedSaving);
 
3762
 
 
3763
    alock.release();
 
3764
 
 
3765
    if (SUCCEEDED(rc))
 
3766
        rc = mParent->saveRegistries(llRegistriesThatNeedSaving);
 
3767
 
 
3768
    return rc;
 
3769
}
 
3770
 
 
3771
STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
 
3772
                                        LONG aDevice, BOOL aPassthrough)
 
3773
{
 
3774
    CheckComArgStrNotEmptyOrNull(aControllerName);
 
3775
 
 
3776
    LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
 
3777
                     aControllerName, aControllerPort, aDevice, aPassthrough));
 
3778
 
 
3779
    AutoCaller autoCaller(this);
 
3780
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3781
 
 
3782
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3783
 
 
3784
    HRESULT rc = checkStateDependency(MutableStateDep);
 
3785
    if (FAILED(rc)) return rc;
 
3786
 
 
3787
    AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
 
3788
 
 
3789
    if (Global::IsOnlineOrTransient(mData->mMachineState))
 
3790
        return setError(VBOX_E_INVALID_VM_STATE,
 
3791
                        tr("Invalid machine state: %s"),
 
3792
                        Global::stringifyMachineState(mData->mMachineState));
 
3793
 
 
3794
    MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
 
3795
                                               aControllerName,
 
3796
                                               aControllerPort,
 
3797
                                               aDevice);
 
3798
    if (!pAttach)
 
3799
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
3800
                        tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
 
3801
                        aDevice, aControllerPort, aControllerName);
 
3802
 
 
3803
 
 
3804
    setModified(IsModified_Storage);
 
3805
    mMediaData.backup();
 
3806
 
 
3807
    AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
 
3808
 
 
3809
    if (pAttach->getType() != DeviceType_DVD)
 
3810
        return setError(E_INVALIDARG,
 
3811
                        tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
 
3812
                        aDevice, aControllerPort, aControllerName);
 
3813
    pAttach->updatePassthrough(!!aPassthrough);
 
3814
 
 
3815
    return S_OK;
 
3816
}
 
3817
 
 
3818
STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
 
3819
                                                 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
 
3820
{
 
3821
    CheckComArgStrNotEmptyOrNull(aControllerName);
 
3822
 
 
3823
    LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
 
3824
                     aControllerName, aControllerPort, aDevice));
 
3825
 
 
3826
    AutoCaller autoCaller(this);
 
3827
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3828
 
 
3829
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3830
 
 
3831
    HRESULT rc = checkStateDependency(MutableStateDep);
 
3832
    if (FAILED(rc)) return rc;
 
3833
 
 
3834
    AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
 
3835
 
 
3836
    if (Global::IsOnlineOrTransient(mData->mMachineState))
 
3837
        return setError(VBOX_E_INVALID_VM_STATE,
 
3838
                        tr("Invalid machine state: %s"),
 
3839
                        Global::stringifyMachineState(mData->mMachineState));
 
3840
 
 
3841
    MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
 
3842
                                               aControllerName,
 
3843
                                               aControllerPort,
 
3844
                                               aDevice);
 
3845
    if (!pAttach)
 
3846
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
3847
                        tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
 
3848
                        aDevice, aControllerPort, aControllerName);
 
3849
 
 
3850
 
 
3851
    setModified(IsModified_Storage);
 
3852
    mMediaData.backup();
 
3853
 
 
3854
    ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
 
3855
    if (aBandwidthGroup && group.isNull())
 
3856
        return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
 
3857
 
 
3858
    AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
 
3859
 
 
3860
    pAttach->updateBandwidthGroup(group);
 
3861
 
 
3862
    return S_OK;
 
3863
}
 
3864
 
 
3865
 
 
3866
STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
 
3867
                                  LONG aControllerPort,
 
3868
                                  LONG aDevice,
 
3869
                                  IMedium *aMedium,
 
3870
                                  BOOL aForce)
 
3871
{
 
3872
    int rc = S_OK;
 
3873
    LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
 
3874
                     aControllerName, aControllerPort, aDevice, aForce));
 
3875
 
 
3876
    CheckComArgStrNotEmptyOrNull(aControllerName);
 
3877
 
 
3878
    AutoCaller autoCaller(this);
 
3879
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3880
 
 
3881
    // request the host lock first, since might be calling Host methods for getting host drives;
 
3882
    // next, protect the media tree all the while we're in here, as well as our member variables
 
3883
    AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
 
3884
                                  this->lockHandle(),
 
3885
                                  &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
3886
 
 
3887
    ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
 
3888
                                                         aControllerName,
 
3889
                                                         aControllerPort,
 
3890
                                                         aDevice);
 
3891
    if (pAttach.isNull())
 
3892
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
3893
                        tr("No drive attached to device slot %d on port %d of controller '%ls'"),
 
3894
                        aDevice, aControllerPort, aControllerName);
 
3895
 
 
3896
    /* Remember previously mounted medium. The medium before taking the
 
3897
     * backup is not necessarily the same thing. */
 
3898
    ComObjPtr<Medium> oldmedium;
 
3899
    oldmedium = pAttach->getMedium();
 
3900
 
 
3901
    ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
 
3902
    if (aMedium && pMedium.isNull())
 
3903
        return setError(E_INVALIDARG, "The given medium pointer is invalid");
 
3904
 
 
3905
    AutoCaller mediumCaller(pMedium);
 
3906
    if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
 
3907
 
 
3908
    AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
 
3909
    if (pMedium)
 
3910
    {
 
3911
        DeviceType_T mediumType = pAttach->getType();
 
3912
        switch (mediumType)
 
3913
        {
 
3914
            case DeviceType_DVD:
 
3915
            case DeviceType_Floppy:
 
3916
            break;
 
3917
 
 
3918
            default:
 
3919
                return setError(VBOX_E_INVALID_OBJECT_STATE,
 
3920
                                tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
 
3921
                                aControllerPort,
 
3922
                                aDevice,
 
3923
                                aControllerName);
 
3924
        }
 
3925
    }
 
3926
 
 
3927
    setModified(IsModified_Storage);
 
3928
    mMediaData.backup();
 
3929
 
 
3930
    GuidList llRegistriesThatNeedSaving;
 
3931
 
 
3932
    {
 
3933
        // The backup operation makes the pAttach reference point to the
 
3934
        // old settings. Re-get the correct reference.
 
3935
        pAttach = findAttachment(mMediaData->mAttachments,
 
3936
                                 aControllerName,
 
3937
                                 aControllerPort,
 
3938
                                 aDevice);
 
3939
        AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
 
3940
        if (!oldmedium.isNull())
 
3941
            oldmedium->removeBackReference(mData->mUuid);
 
3942
        if (!pMedium.isNull())
 
3943
        {
 
3944
            pMedium->addBackReference(mData->mUuid);
 
3945
 
 
3946
            // and decide which medium registry to use now that the medium is attached:
 
3947
            Guid uuid;
 
3948
            if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
 
3949
                // machine XML is VirtualBox 4.0 or higher:
 
3950
                uuid = getId();     // machine UUID
 
3951
            else
 
3952
                uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
 
3953
 
 
3954
            if (pMedium->addRegistry(uuid))
 
3955
                // registry actually changed:
 
3956
                mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, uuid);
 
3957
        }
 
3958
 
 
3959
        pAttach->updateMedium(pMedium);
 
3960
    }
 
3961
 
 
3962
    setModified(IsModified_Storage);
 
3963
 
 
3964
    mediumLock.release();
 
3965
    multiLock.release();
 
3966
    rc = onMediumChange(pAttach, aForce);
 
3967
    multiLock.acquire();
 
3968
    mediumLock.acquire();
 
3969
 
 
3970
    /* On error roll back this change only. */
 
3971
    if (FAILED(rc))
 
3972
    {
 
3973
        if (!pMedium.isNull())
 
3974
            pMedium->removeBackReference(mData->mUuid);
 
3975
        pAttach = findAttachment(mMediaData->mAttachments,
 
3976
                                 aControllerName,
 
3977
                                 aControllerPort,
 
3978
                                 aDevice);
 
3979
        /* If the attachment is gone in the meantime, bail out. */
 
3980
        if (pAttach.isNull())
 
3981
            return rc;
 
3982
        AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
 
3983
        if (!oldmedium.isNull())
 
3984
            oldmedium->addBackReference(mData->mUuid);
 
3985
        pAttach->updateMedium(oldmedium);
 
3986
    }
 
3987
 
 
3988
    mediumLock.release();
 
3989
    multiLock.release();
 
3990
 
 
3991
    mParent->saveRegistries(llRegistriesThatNeedSaving);
 
3992
 
 
3993
    return rc;
 
3994
}
 
3995
 
 
3996
STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
 
3997
                                LONG aControllerPort,
 
3998
                                LONG aDevice,
 
3999
                                IMedium **aMedium)
 
4000
{
 
4001
    LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
 
4002
                     aControllerName, aControllerPort, aDevice));
 
4003
 
 
4004
    CheckComArgStrNotEmptyOrNull(aControllerName);
 
4005
    CheckComArgOutPointerValid(aMedium);
 
4006
 
 
4007
    AutoCaller autoCaller(this);
 
4008
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4009
 
 
4010
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4011
 
 
4012
    *aMedium = NULL;
 
4013
 
 
4014
    ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
 
4015
                                                         aControllerName,
 
4016
                                                         aControllerPort,
 
4017
                                                         aDevice);
 
4018
    if (pAttach.isNull())
 
4019
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
4020
                        tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
 
4021
                        aDevice, aControllerPort, aControllerName);
 
4022
 
 
4023
    pAttach->getMedium().queryInterfaceTo(aMedium);
 
4024
 
 
4025
    return S_OK;
 
4026
}
 
4027
 
 
4028
STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
 
4029
{
 
4030
    CheckComArgOutPointerValid(port);
 
4031
    CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
 
4032
 
 
4033
    AutoCaller autoCaller(this);
 
4034
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4035
 
 
4036
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4037
 
 
4038
    mSerialPorts[slot].queryInterfaceTo(port);
 
4039
 
 
4040
    return S_OK;
 
4041
}
 
4042
 
 
4043
STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
 
4044
{
 
4045
    CheckComArgOutPointerValid(port);
 
4046
    CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
 
4047
 
 
4048
    AutoCaller autoCaller(this);
 
4049
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4050
 
 
4051
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4052
 
 
4053
    mParallelPorts[slot].queryInterfaceTo(port);
 
4054
 
 
4055
    return S_OK;
 
4056
}
 
4057
 
 
4058
STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
 
4059
{
 
4060
    CheckComArgOutPointerValid(adapter);
 
4061
    CheckComArgExpr(slot, slot < RT_ELEMENTS(mNetworkAdapters));
 
4062
 
 
4063
    AutoCaller autoCaller(this);
 
4064
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4065
 
 
4066
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4067
 
 
4068
    mNetworkAdapters[slot].queryInterfaceTo(adapter);
 
4069
 
 
4070
    return S_OK;
 
4071
}
 
4072
 
 
4073
STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
 
4074
{
 
4075
    if (ComSafeArrayOutIsNull(aKeys))
 
4076
        return E_POINTER;
 
4077
 
 
4078
    AutoCaller autoCaller(this);
 
4079
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4080
 
 
4081
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4082
 
 
4083
    com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
 
4084
    int i = 0;
 
4085
    for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
 
4086
         it != mData->pMachineConfigFile->mapExtraDataItems.end();
 
4087
         ++it, ++i)
 
4088
    {
 
4089
        const Utf8Str &strKey = it->first;
 
4090
        strKey.cloneTo(&saKeys[i]);
 
4091
    }
 
4092
    saKeys.detachTo(ComSafeArrayOutArg(aKeys));
 
4093
 
 
4094
    return S_OK;
 
4095
  }
 
4096
 
 
4097
  /**
 
4098
   *  @note Locks this object for reading.
 
4099
   */
 
4100
STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
 
4101
                                   BSTR *aValue)
 
4102
{
 
4103
    CheckComArgStrNotEmptyOrNull(aKey);
 
4104
    CheckComArgOutPointerValid(aValue);
 
4105
 
 
4106
    AutoCaller autoCaller(this);
 
4107
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4108
 
 
4109
    /* start with nothing found */
 
4110
    Bstr bstrResult("");
 
4111
 
 
4112
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4113
 
 
4114
    settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
 
4115
    if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
 
4116
        // found:
 
4117
        bstrResult = it->second; // source is a Utf8Str
 
4118
 
 
4119
    /* return the result to caller (may be empty) */
 
4120
    bstrResult.cloneTo(aValue);
 
4121
 
 
4122
    return S_OK;
 
4123
}
 
4124
 
 
4125
  /**
 
4126
   *  @note Locks mParent for writing + this object for writing.
 
4127
   */
 
4128
STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
 
4129
{
 
4130
    CheckComArgStrNotEmptyOrNull(aKey);
 
4131
 
 
4132
    AutoCaller autoCaller(this);
 
4133
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4134
 
 
4135
    Utf8Str strKey(aKey);
 
4136
    Utf8Str strValue(aValue);
 
4137
    Utf8Str strOldValue;            // empty
 
4138
 
 
4139
    // locking note: we only hold the read lock briefly to look up the old value,
 
4140
    // then release it and call the onExtraCanChange callbacks. There is a small
 
4141
    // chance of a race insofar as the callback might be called twice if two callers
 
4142
    // change the same key at the same time, but that's a much better solution
 
4143
    // than the deadlock we had here before. The actual changing of the extradata
 
4144
    // is then performed under the write lock and race-free.
 
4145
 
 
4146
    // look up the old value first; if nothing has changed then we need not do anything
 
4147
    {
 
4148
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
 
4149
        settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
 
4150
        if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
 
4151
            strOldValue = it->second;
 
4152
    }
 
4153
 
 
4154
    bool fChanged;
 
4155
    if ((fChanged = (strOldValue != strValue)))
 
4156
    {
 
4157
        // ask for permission from all listeners outside the locks;
 
4158
        // onExtraDataCanChange() only briefly requests the VirtualBox
 
4159
        // lock to copy the list of callbacks to invoke
 
4160
        Bstr error;
 
4161
        Bstr bstrValue(aValue);
 
4162
 
 
4163
        if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
 
4164
        {
 
4165
            const char *sep = error.isEmpty() ? "" : ": ";
 
4166
            CBSTR err = error.raw();
 
4167
            LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
 
4168
                            sep, err));
 
4169
            return setError(E_ACCESSDENIED,
 
4170
                            tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
 
4171
                            aKey,
 
4172
                            bstrValue.raw(),
 
4173
                            sep,
 
4174
                            err);
 
4175
        }
 
4176
 
 
4177
        // data is changing and change not vetoed: then write it out under the lock
 
4178
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4179
 
 
4180
        if (isSnapshotMachine())
 
4181
        {
 
4182
            HRESULT rc = checkStateDependency(MutableStateDep);
 
4183
            if (FAILED(rc)) return rc;
 
4184
        }
 
4185
 
 
4186
        if (strValue.isEmpty())
 
4187
            mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
 
4188
        else
 
4189
            mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
 
4190
                // creates a new key if needed
 
4191
 
 
4192
        bool fNeedsGlobalSaveSettings = false;
 
4193
        saveSettings(&fNeedsGlobalSaveSettings);
 
4194
 
 
4195
        if (fNeedsGlobalSaveSettings)
 
4196
        {
 
4197
            // save the global settings; for that we should hold only the VirtualBox lock
 
4198
            alock.release();
 
4199
            AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
 
4200
            mParent->saveSettings();
 
4201
        }
 
4202
    }
 
4203
 
 
4204
    // fire notification outside the lock
 
4205
    if (fChanged)
 
4206
        mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
 
4207
 
 
4208
    return S_OK;
 
4209
}
 
4210
 
 
4211
STDMETHODIMP Machine::SaveSettings()
 
4212
{
 
4213
    AutoCaller autoCaller(this);
 
4214
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4215
 
 
4216
    AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
 
4217
 
 
4218
    /* when there was auto-conversion, we want to save the file even if
 
4219
     * the VM is saved */
 
4220
    HRESULT rc = checkStateDependency(MutableStateDep);
 
4221
    if (FAILED(rc)) return rc;
 
4222
 
 
4223
    /* the settings file path may never be null */
 
4224
    ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
 
4225
 
 
4226
    /* save all VM data excluding snapshots */
 
4227
    bool fNeedsGlobalSaveSettings = false;
 
4228
    rc = saveSettings(&fNeedsGlobalSaveSettings);
 
4229
    mlock.release();
 
4230
 
 
4231
    if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
 
4232
    {
 
4233
        // save the global settings; for that we should hold only the VirtualBox lock
 
4234
        AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
 
4235
        rc = mParent->saveSettings();
 
4236
    }
 
4237
 
 
4238
    return rc;
 
4239
}
 
4240
 
 
4241
STDMETHODIMP Machine::DiscardSettings()
 
4242
{
 
4243
    AutoCaller autoCaller(this);
 
4244
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4245
 
 
4246
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4247
 
 
4248
    HRESULT rc = checkStateDependency(MutableStateDep);
 
4249
    if (FAILED(rc)) return rc;
 
4250
 
 
4251
    /*
 
4252
     *  during this rollback, the session will be notified if data has
 
4253
     *  been actually changed
 
4254
     */
 
4255
    rollback(true /* aNotify */);
 
4256
 
 
4257
    return S_OK;
 
4258
}
 
4259
 
 
4260
/** @note Locks objects! */
 
4261
STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
 
4262
                                 ComSafeArrayOut(IMedium*, aMedia))
 
4263
{
 
4264
    // use AutoLimitedCaller because this call is valid on inaccessible machines as well
 
4265
    AutoLimitedCaller autoCaller(this);
 
4266
    AssertComRCReturnRC(autoCaller.rc());
 
4267
 
 
4268
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4269
 
 
4270
    Guid id(getId());
 
4271
 
 
4272
    if (mData->mSession.mState != SessionState_Unlocked)
 
4273
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
4274
                        tr("Cannot unregister the machine '%s' while it is locked"),
 
4275
                        mUserData->s.strName.c_str());
 
4276
 
 
4277
    // wait for state dependents to drop to zero
 
4278
    ensureNoStateDependencies();
 
4279
 
 
4280
    if (!mData->mAccessible)
 
4281
    {
 
4282
        // inaccessible maschines can only be unregistered; uninitialize ourselves
 
4283
        // here because currently there may be no unregistered that are inaccessible
 
4284
        // (this state combination is not supported). Note releasing the caller and
 
4285
        // leaving the lock before calling uninit()
 
4286
        alock.leave();
 
4287
        autoCaller.release();
 
4288
 
 
4289
        uninit();
 
4290
 
 
4291
        mParent->unregisterMachine(this, id);
 
4292
            // calls VirtualBox::saveSettings()
 
4293
 
 
4294
        return S_OK;
 
4295
    }
 
4296
 
 
4297
    HRESULT rc = S_OK;
 
4298
 
 
4299
    // discard saved state
 
4300
    if (mData->mMachineState == MachineState_Saved)
 
4301
    {
 
4302
        // add the saved state file to the list of files the caller should delete
 
4303
        Assert(!mSSData->mStateFilePath.isEmpty());
 
4304
        mData->llFilesToDelete.push_back(mSSData->mStateFilePath);
 
4305
 
 
4306
        mSSData->mStateFilePath.setNull();
 
4307
 
 
4308
        // unconditionally set the machine state to powered off, we now
 
4309
        // know no session has locked the machine
 
4310
        mData->mMachineState = MachineState_PoweredOff;
 
4311
    }
 
4312
 
 
4313
    size_t cSnapshots = 0;
 
4314
    if (mData->mFirstSnapshot)
 
4315
        cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
 
4316
    if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
 
4317
        // fail now before we start detaching media
 
4318
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
4319
                        tr("Cannot unregister the machine '%s' because it has %d snapshots"),
 
4320
                           mUserData->s.strName.c_str(), cSnapshots);
 
4321
 
 
4322
    // this list collects the medium objects from all medium attachments
 
4323
    // which got detached from the machine and its snapshots, in the following
 
4324
    // order:
 
4325
    // 1) media from machine attachments (these have the "leaf" attachments with snapshots
 
4326
    //    and must be closed first, or closing the parents will fail because they will
 
4327
    //    children);
 
4328
    // 2) media from the youngest snapshots followed by those from the parent snapshots until
 
4329
    //    the root ("first") snapshot of the machine
 
4330
    // This order allows for closing the media on this list from the beginning to the end
 
4331
    // without getting "media in use" errors.
 
4332
    MediaList llMedia;
 
4333
 
 
4334
    if (    !mMediaData.isNull()      // can be NULL if machine is inaccessible
 
4335
         && mMediaData->mAttachments.size()
 
4336
       )
 
4337
    {
 
4338
        // we have media attachments: detach them all and add the Medium objects to our list
 
4339
        if (cleanupMode != CleanupMode_UnregisterOnly)
 
4340
            detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
 
4341
        else
 
4342
            return setError(VBOX_E_INVALID_OBJECT_STATE,
 
4343
                            tr("Cannot unregister the machine '%s' because it has %d media attachments"),
 
4344
                            mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
 
4345
    }
 
4346
 
 
4347
    if (cSnapshots)
 
4348
    {
 
4349
        // autoCleanup must be true here, or we would have failed above
 
4350
 
 
4351
        // add the media from the medium attachments of the snapshots to llMedia
 
4352
        // as well, after the "main" machine media; Snapshot::uninitRecursively()
 
4353
        // calls Machine::detachAllMedia() for the snapshot machine, recursing
 
4354
        // into the children first
 
4355
 
 
4356
        // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
 
4357
        MachineState_T oldState = mData->mMachineState;
 
4358
        mData->mMachineState = MachineState_DeletingSnapshot;
 
4359
 
 
4360
        // make a copy of the first snapshot so the refcount does not drop to 0
 
4361
        // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
 
4362
        // because of the AutoCaller voodoo)
 
4363
        ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
 
4364
 
 
4365
        // GO!
 
4366
        pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
 
4367
 
 
4368
        mData->mMachineState = oldState;
 
4369
    }
 
4370
 
 
4371
    if (FAILED(rc))
 
4372
    {
 
4373
        rollbackMedia();
 
4374
        return rc;
 
4375
    }
 
4376
 
 
4377
    // commit all the media changes made above
 
4378
    commitMedia();
 
4379
 
 
4380
    mData->mRegistered = false;
 
4381
 
 
4382
    // machine lock no longer needed
 
4383
    alock.release();
 
4384
 
 
4385
    // return media to caller
 
4386
    SafeIfaceArray<IMedium> sfaMedia(llMedia);
 
4387
    sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
 
4388
 
 
4389
    mParent->unregisterMachine(this, id);
 
4390
            // calls VirtualBox::saveSettings()
 
4391
 
 
4392
    return S_OK;
 
4393
}
 
4394
 
 
4395
struct Machine::DeleteTask
 
4396
{
 
4397
    ComObjPtr<Machine>          pMachine;
 
4398
    std::list<Utf8Str>          llFilesToDelete;
 
4399
    ComObjPtr<Progress>         pProgress;
 
4400
    GuidList                    llRegistriesThatNeedSaving;
 
4401
};
 
4402
 
 
4403
STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
 
4404
{
 
4405
    LogFlowFuncEnter();
 
4406
 
 
4407
    AutoCaller autoCaller(this);
 
4408
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4409
 
 
4410
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4411
 
 
4412
    HRESULT rc = checkStateDependency(MutableStateDep);
 
4413
    if (FAILED(rc)) return rc;
 
4414
 
 
4415
    if (mData->mRegistered)
 
4416
        return setError(VBOX_E_INVALID_VM_STATE,
 
4417
                        tr("Cannot delete settings of a registered machine"));
 
4418
 
 
4419
    DeleteTask *pTask = new DeleteTask;
 
4420
    pTask->pMachine = this;
 
4421
    com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
 
4422
 
 
4423
    // collect files to delete
 
4424
    pTask->llFilesToDelete = mData->llFilesToDelete;            // saved states pushed here by Unregister()
 
4425
 
 
4426
    for (size_t i = 0; i < sfaMedia.size(); ++i)
 
4427
    {
 
4428
        IMedium *pIMedium(sfaMedia[i]);
 
4429
        Medium *pMedium = static_cast<Medium*>(pIMedium);
 
4430
        AutoCaller mediumAutoCaller(pMedium);
 
4431
        if (FAILED(mediumAutoCaller.rc())) return mediumAutoCaller.rc();
 
4432
 
 
4433
        Utf8Str bstrLocation = pMedium->getLocationFull();
 
4434
 
 
4435
        bool fDoesMediumNeedFileDeletion = pMedium->isMediumFormatFile();
 
4436
 
 
4437
        // close the medium now; if that succeeds, then that means the medium is no longer
 
4438
        // in use and we can add it to the list of files to delete
 
4439
        rc = pMedium->close(&pTask->llRegistriesThatNeedSaving,
 
4440
                            mediumAutoCaller);
 
4441
        if (SUCCEEDED(rc) && fDoesMediumNeedFileDeletion)
 
4442
            pTask->llFilesToDelete.push_back(bstrLocation);
 
4443
    }
 
4444
    if (mData->pMachineConfigFile->fileExists())
 
4445
        pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
 
4446
 
 
4447
    pTask->pProgress.createObject();
 
4448
    pTask->pProgress->init(getVirtualBox(),
 
4449
                           static_cast<IMachine*>(this) /* aInitiator */,
 
4450
                           Bstr(tr("Deleting files")).raw(),
 
4451
                           true /* fCancellable */,
 
4452
                           pTask->llFilesToDelete.size() + 1,   // cOperations
 
4453
                           BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
 
4454
 
 
4455
    int vrc = RTThreadCreate(NULL,
 
4456
                             Machine::deleteThread,
 
4457
                             (void*)pTask,
 
4458
                             0,
 
4459
                             RTTHREADTYPE_MAIN_WORKER,
 
4460
                             0,
 
4461
                             "MachineDelete");
 
4462
 
 
4463
    pTask->pProgress.queryInterfaceTo(aProgress);
 
4464
 
 
4465
    if (RT_FAILURE(vrc))
 
4466
    {
 
4467
        delete pTask;
 
4468
        return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
 
4469
    }
 
4470
 
 
4471
    LogFlowFuncLeave();
 
4472
 
 
4473
    return S_OK;
 
4474
}
 
4475
 
 
4476
/**
 
4477
 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
 
4478
 * calls Machine::deleteTaskWorker() on the actual machine object.
 
4479
 * @param Thread
 
4480
 * @param pvUser
 
4481
 * @return
 
4482
 */
 
4483
/*static*/
 
4484
DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
 
4485
{
 
4486
    LogFlowFuncEnter();
 
4487
 
 
4488
    DeleteTask *pTask = (DeleteTask*)pvUser;
 
4489
    Assert(pTask);
 
4490
    Assert(pTask->pMachine);
 
4491
    Assert(pTask->pProgress);
 
4492
 
 
4493
    HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
 
4494
    pTask->pProgress->notifyComplete(rc);
 
4495
 
 
4496
    delete pTask;
 
4497
 
 
4498
    LogFlowFuncLeave();
 
4499
 
 
4500
    NOREF(Thread);
 
4501
 
 
4502
    return VINF_SUCCESS;
 
4503
}
 
4504
 
 
4505
/**
 
4506
 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
 
4507
 * @param task
 
4508
 * @return
 
4509
 */
 
4510
HRESULT Machine::deleteTaskWorker(DeleteTask &task)
 
4511
{
 
4512
    AutoCaller autoCaller(this);
 
4513
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4514
 
 
4515
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4516
 
 
4517
    ULONG uLogHistoryCount = 3;
 
4518
    ComPtr<ISystemProperties> systemProperties;
 
4519
    mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
 
4520
    if (!systemProperties.isNull())
 
4521
        systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
 
4522
 
 
4523
    // delete the files pushed on the task list by Machine::Delete()
 
4524
    // (this includes saved states of the machine and snapshots and
 
4525
    // medium storage files from the IMedium list passed in, and the
 
4526
    // machine XML file)
 
4527
    std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin();
 
4528
    while (it != task.llFilesToDelete.end())
 
4529
    {
 
4530
        const Utf8Str &strFile = *it;
 
4531
        LogFunc(("Deleting file %s\n", strFile.c_str()));
 
4532
        RTFileDelete(strFile.c_str());
 
4533
 
 
4534
        ++it;
 
4535
        if (it == task.llFilesToDelete.end())
 
4536
        {
 
4537
            task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
 
4538
            break;
 
4539
        }
 
4540
 
 
4541
        task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
 
4542
    }
 
4543
 
 
4544
    /* delete the settings only when the file actually exists */
 
4545
    if (mData->pMachineConfigFile->fileExists())
 
4546
    {
 
4547
        /* Delete any backup or uncommitted XML files. Ignore failures.
 
4548
           See the fSafe parameter of xml::XmlFileWriter::write for details. */
 
4549
        /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
 
4550
        Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
 
4551
        RTFileDelete(otherXml.c_str());
 
4552
        otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
 
4553
        RTFileDelete(otherXml.c_str());
 
4554
 
 
4555
        /* delete the Logs folder, nothing important should be left
 
4556
         * there (we don't check for errors because the user might have
 
4557
         * some private files there that we don't want to delete) */
 
4558
        Utf8Str logFolder;
 
4559
        getLogFolder(logFolder);
 
4560
        Assert(logFolder.length());
 
4561
        if (RTDirExists(logFolder.c_str()))
 
4562
        {
 
4563
            /* Delete all VBox.log[.N] files from the Logs folder
 
4564
             * (this must be in sync with the rotation logic in
 
4565
             * Console::powerUpThread()). Also, delete the VBox.png[.N]
 
4566
             * files that may have been created by the GUI. */
 
4567
            Utf8Str log = Utf8StrFmt("%s%cVBox.log",
 
4568
                                     logFolder.c_str(), RTPATH_DELIMITER);
 
4569
            RTFileDelete(log.c_str());
 
4570
            log = Utf8StrFmt("%s%cVBox.png",
 
4571
                             logFolder.c_str(), RTPATH_DELIMITER);
 
4572
            RTFileDelete(log.c_str());
 
4573
            for (int i = uLogHistoryCount; i > 0; i--)
 
4574
            {
 
4575
                log = Utf8StrFmt("%s%cVBox.log.%d",
 
4576
                                 logFolder.c_str(), RTPATH_DELIMITER, i);
 
4577
                RTFileDelete(log.c_str());
 
4578
                log = Utf8StrFmt("%s%cVBox.png.%d",
 
4579
                                 logFolder.c_str(), RTPATH_DELIMITER, i);
 
4580
                RTFileDelete(log.c_str());
 
4581
            }
 
4582
 
 
4583
            RTDirRemove(logFolder.c_str());
 
4584
        }
 
4585
 
 
4586
        /* delete the Snapshots folder, nothing important should be left
 
4587
         * there (we don't check for errors because the user might have
 
4588
         * some private files there that we don't want to delete) */
 
4589
        Utf8Str strFullSnapshotFolder;
 
4590
        calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
 
4591
        Assert(!strFullSnapshotFolder.isEmpty());
 
4592
        if (RTDirExists(strFullSnapshotFolder.c_str()))
 
4593
            RTDirRemove(strFullSnapshotFolder.c_str());
 
4594
 
 
4595
        // delete the directory that contains the settings file, but only
 
4596
        // if it matches the VM name
 
4597
        Utf8Str settingsDir;
 
4598
        if (isInOwnDir(&settingsDir))
 
4599
            RTDirRemove(settingsDir.c_str());
 
4600
    }
 
4601
 
 
4602
    alock.release();
 
4603
 
 
4604
    mParent->saveRegistries(task.llRegistriesThatNeedSaving);
 
4605
 
 
4606
    return S_OK;
 
4607
}
 
4608
 
 
4609
STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
 
4610
{
 
4611
    CheckComArgOutPointerValid(aSnapshot);
 
4612
 
 
4613
    AutoCaller autoCaller(this);
 
4614
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4615
 
 
4616
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4617
 
 
4618
    ComObjPtr<Snapshot> pSnapshot;
 
4619
    HRESULT rc;
 
4620
 
 
4621
    if (!aNameOrId || !*aNameOrId)
 
4622
        // null case (caller wants root snapshot): findSnapshotById() handles this
 
4623
        rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
 
4624
    else
 
4625
    {
 
4626
        Guid uuid(aNameOrId);
 
4627
        if (!uuid.isEmpty())
 
4628
            rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
 
4629
        else
 
4630
            rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
 
4631
    }
 
4632
    pSnapshot.queryInterfaceTo(aSnapshot);
 
4633
 
 
4634
    return rc;
 
4635
}
 
4636
 
 
4637
STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
 
4638
{
 
4639
    CheckComArgStrNotEmptyOrNull(aName);
 
4640
    CheckComArgStrNotEmptyOrNull(aHostPath);
 
4641
 
 
4642
    AutoCaller autoCaller(this);
 
4643
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4644
 
 
4645
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4646
 
 
4647
    HRESULT rc = checkStateDependency(MutableStateDep);
 
4648
    if (FAILED(rc)) return rc;
 
4649
 
 
4650
    ComObjPtr<SharedFolder> sharedFolder;
 
4651
    rc = findSharedFolder(aName, sharedFolder, false /* aSetError */);
 
4652
    if (SUCCEEDED(rc))
 
4653
        return setError(VBOX_E_OBJECT_IN_USE,
 
4654
                        tr("Shared folder named '%ls' already exists"),
 
4655
                        aName);
 
4656
 
 
4657
    sharedFolder.createObject();
 
4658
    rc = sharedFolder->init(getMachine(), aName, aHostPath, aWritable, aAutoMount);
 
4659
    if (FAILED(rc)) return rc;
 
4660
 
 
4661
    setModified(IsModified_SharedFolders);
 
4662
    mHWData.backup();
 
4663
    mHWData->mSharedFolders.push_back(sharedFolder);
 
4664
 
 
4665
    /* inform the direct session if any */
 
4666
    alock.leave();
 
4667
    onSharedFolderChange();
 
4668
 
 
4669
    return S_OK;
 
4670
}
 
4671
 
 
4672
STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
 
4673
{
 
4674
    CheckComArgStrNotEmptyOrNull(aName);
 
4675
 
 
4676
    AutoCaller autoCaller(this);
 
4677
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4678
 
 
4679
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4680
 
 
4681
    HRESULT rc = checkStateDependency(MutableStateDep);
 
4682
    if (FAILED(rc)) return rc;
 
4683
 
 
4684
    ComObjPtr<SharedFolder> sharedFolder;
 
4685
    rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
 
4686
    if (FAILED(rc)) return rc;
 
4687
 
 
4688
    setModified(IsModified_SharedFolders);
 
4689
    mHWData.backup();
 
4690
    mHWData->mSharedFolders.remove(sharedFolder);
 
4691
 
 
4692
    /* inform the direct session if any */
 
4693
    alock.leave();
 
4694
    onSharedFolderChange();
 
4695
 
 
4696
    return S_OK;
 
4697
}
 
4698
 
 
4699
STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
 
4700
{
 
4701
    CheckComArgOutPointerValid(aCanShow);
 
4702
 
 
4703
    /* start with No */
 
4704
    *aCanShow = FALSE;
 
4705
 
 
4706
    AutoCaller autoCaller(this);
 
4707
    AssertComRCReturnRC(autoCaller.rc());
 
4708
 
 
4709
    ComPtr<IInternalSessionControl> directControl;
 
4710
    {
 
4711
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4712
 
 
4713
        if (mData->mSession.mState != SessionState_Locked)
 
4714
            return setError(VBOX_E_INVALID_VM_STATE,
 
4715
                            tr("Machine is not locked for session (session state: %s)"),
 
4716
                            Global::stringifySessionState(mData->mSession.mState));
 
4717
 
 
4718
        directControl = mData->mSession.mDirectControl;
 
4719
    }
 
4720
 
 
4721
    /* ignore calls made after #OnSessionEnd() is called */
 
4722
    if (!directControl)
 
4723
        return S_OK;
 
4724
 
 
4725
    LONG64 dummy;
 
4726
    return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
 
4727
}
 
4728
 
 
4729
STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
 
4730
{
 
4731
    CheckComArgOutPointerValid(aWinId);
 
4732
 
 
4733
    AutoCaller autoCaller(this);
 
4734
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
4735
 
 
4736
    ComPtr<IInternalSessionControl> directControl;
 
4737
    {
 
4738
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4739
 
 
4740
        if (mData->mSession.mState != SessionState_Locked)
 
4741
            return setError(E_FAIL,
 
4742
                            tr("Machine is not locked for session (session state: %s)"),
 
4743
                            Global::stringifySessionState(mData->mSession.mState));
 
4744
 
 
4745
        directControl = mData->mSession.mDirectControl;
 
4746
    }
 
4747
 
 
4748
    /* ignore calls made after #OnSessionEnd() is called */
 
4749
    if (!directControl)
 
4750
        return S_OK;
 
4751
 
 
4752
    BOOL dummy;
 
4753
    return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
 
4754
}
 
4755
 
 
4756
#ifdef VBOX_WITH_GUEST_PROPS
 
4757
/**
 
4758
 * Look up a guest property in VBoxSVC's internal structures.
 
4759
 */
 
4760
HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
 
4761
                                             BSTR *aValue,
 
4762
                                             LONG64 *aTimestamp,
 
4763
                                             BSTR *aFlags) const
 
4764
{
 
4765
    using namespace guestProp;
 
4766
 
 
4767
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4768
    Utf8Str strName(aName);
 
4769
    HWData::GuestPropertyList::const_iterator it;
 
4770
 
 
4771
    for (it = mHWData->mGuestProperties.begin();
 
4772
         it != mHWData->mGuestProperties.end(); ++it)
 
4773
    {
 
4774
        if (it->strName == strName)
 
4775
        {
 
4776
            char szFlags[MAX_FLAGS_LEN + 1];
 
4777
            it->strValue.cloneTo(aValue);
 
4778
            *aTimestamp = it->mTimestamp;
 
4779
            writeFlags(it->mFlags, szFlags);
 
4780
            Bstr(szFlags).cloneTo(aFlags);
 
4781
            break;
 
4782
        }
 
4783
    }
 
4784
    return S_OK;
 
4785
}
 
4786
 
 
4787
/**
 
4788
 * Query the VM that a guest property belongs to for the property.
 
4789
 * @returns E_ACCESSDENIED if the VM process is not available or not
 
4790
 *          currently handling queries and the lookup should then be done in
 
4791
 *          VBoxSVC.
 
4792
 */
 
4793
HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
 
4794
                                        BSTR *aValue,
 
4795
                                        LONG64 *aTimestamp,
 
4796
                                        BSTR *aFlags) const
 
4797
{
 
4798
    HRESULT rc;
 
4799
    ComPtr<IInternalSessionControl> directControl;
 
4800
    directControl = mData->mSession.mDirectControl;
 
4801
 
 
4802
    /* fail if we were called after #OnSessionEnd() is called.  This is a
 
4803
     * silly race condition. */
 
4804
 
 
4805
    if (!directControl)
 
4806
        rc = E_ACCESSDENIED;
 
4807
    else
 
4808
        rc = directControl->AccessGuestProperty(aName, NULL, NULL,
 
4809
                                                false /* isSetter */,
 
4810
                                                aValue, aTimestamp, aFlags);
 
4811
    return rc;
 
4812
}
 
4813
#endif // VBOX_WITH_GUEST_PROPS
 
4814
 
 
4815
STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
 
4816
                                       BSTR *aValue,
 
4817
                                       LONG64 *aTimestamp,
 
4818
                                       BSTR *aFlags)
 
4819
{
 
4820
#ifndef VBOX_WITH_GUEST_PROPS
 
4821
    ReturnComNotImplemented();
 
4822
#else // VBOX_WITH_GUEST_PROPS
 
4823
    CheckComArgStrNotEmptyOrNull(aName);
 
4824
    CheckComArgOutPointerValid(aValue);
 
4825
    CheckComArgOutPointerValid(aTimestamp);
 
4826
    CheckComArgOutPointerValid(aFlags);
 
4827
 
 
4828
    AutoCaller autoCaller(this);
 
4829
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4830
 
 
4831
    HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
 
4832
    if (rc == E_ACCESSDENIED)
 
4833
        /* The VM is not running or the service is not (yet) accessible */
 
4834
        rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
 
4835
    return rc;
 
4836
#endif // VBOX_WITH_GUEST_PROPS
 
4837
}
 
4838
 
 
4839
STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
 
4840
{
 
4841
    LONG64 dummyTimestamp;
 
4842
    Bstr dummyFlags;
 
4843
    return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
 
4844
}
 
4845
 
 
4846
STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
 
4847
{
 
4848
    Bstr dummyValue;
 
4849
    Bstr dummyFlags;
 
4850
    return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
 
4851
}
 
4852
 
 
4853
#ifdef VBOX_WITH_GUEST_PROPS
 
4854
/**
 
4855
 * Set a guest property in VBoxSVC's internal structures.
 
4856
 */
 
4857
HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
 
4858
                                           IN_BSTR aFlags)
 
4859
{
 
4860
    using namespace guestProp;
 
4861
 
 
4862
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4863
    HRESULT rc = S_OK;
 
4864
    HWData::GuestProperty property;
 
4865
    property.mFlags = NILFLAG;
 
4866
    bool found = false;
 
4867
 
 
4868
    rc = checkStateDependency(MutableStateDep);
 
4869
    if (FAILED(rc)) return rc;
 
4870
 
 
4871
    try
 
4872
    {
 
4873
        Utf8Str utf8Name(aName);
 
4874
        Utf8Str utf8Flags(aFlags);
 
4875
        uint32_t fFlags = NILFLAG;
 
4876
        if (    (aFlags != NULL)
 
4877
             && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
 
4878
           )
 
4879
            return setError(E_INVALIDARG,
 
4880
                            tr("Invalid flag values: '%ls'"),
 
4881
                            aFlags);
 
4882
 
 
4883
        /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
 
4884
         *                know, this is simple and do an OK job atm.) */
 
4885
        HWData::GuestPropertyList::iterator it;
 
4886
        for (it = mHWData->mGuestProperties.begin();
 
4887
             it != mHWData->mGuestProperties.end(); ++it)
 
4888
            if (it->strName == utf8Name)
 
4889
            {
 
4890
                property = *it;
 
4891
                if (it->mFlags & (RDONLYHOST))
 
4892
                    rc = setError(E_ACCESSDENIED,
 
4893
                                  tr("The property '%ls' cannot be changed by the host"),
 
4894
                                  aName);
 
4895
                else
 
4896
                {
 
4897
                    setModified(IsModified_MachineData);
 
4898
                    mHWData.backup();           // @todo r=dj backup in a loop?!?
 
4899
 
 
4900
                    /* The backup() operation invalidates our iterator, so
 
4901
                    * get a new one. */
 
4902
                    for (it = mHWData->mGuestProperties.begin();
 
4903
                         it->strName != utf8Name;
 
4904
                         ++it)
 
4905
                        ;
 
4906
                    mHWData->mGuestProperties.erase(it);
 
4907
                }
 
4908
                found = true;
 
4909
                break;
 
4910
            }
 
4911
        if (found && SUCCEEDED(rc))
 
4912
        {
 
4913
            if (*aValue)
 
4914
            {
 
4915
                RTTIMESPEC time;
 
4916
                property.strValue = aValue;
 
4917
                property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
 
4918
                if (aFlags != NULL)
 
4919
                    property.mFlags = fFlags;
 
4920
                mHWData->mGuestProperties.push_back(property);
 
4921
            }
 
4922
        }
 
4923
        else if (SUCCEEDED(rc) && *aValue)
 
4924
        {
 
4925
            RTTIMESPEC time;
 
4926
            setModified(IsModified_MachineData);
 
4927
            mHWData.backup();
 
4928
            property.strName = aName;
 
4929
            property.strValue = aValue;
 
4930
            property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
 
4931
            property.mFlags = fFlags;
 
4932
            mHWData->mGuestProperties.push_back(property);
 
4933
        }
 
4934
        if (   SUCCEEDED(rc)
 
4935
            && (   mHWData->mGuestPropertyNotificationPatterns.isEmpty()
 
4936
                || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
 
4937
                                                RTSTR_MAX,
 
4938
                                                utf8Name.c_str(),
 
4939
                                                RTSTR_MAX,
 
4940
                                                NULL)
 
4941
               )
 
4942
           )
 
4943
        {
 
4944
            /** @todo r=bird: Why aren't we leaving the lock here?  The
 
4945
             *                same code in PushGuestProperty does... */
 
4946
            mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags);
 
4947
        }
 
4948
    }
 
4949
    catch (std::bad_alloc &)
 
4950
    {
 
4951
        rc = E_OUTOFMEMORY;
 
4952
    }
 
4953
 
 
4954
    return rc;
 
4955
}
 
4956
 
 
4957
/**
 
4958
 * Set a property on the VM that that property belongs to.
 
4959
 * @returns E_ACCESSDENIED if the VM process is not available or not
 
4960
 *          currently handling queries and the setting should then be done in
 
4961
 *          VBoxSVC.
 
4962
 */
 
4963
HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
 
4964
                                      IN_BSTR aFlags)
 
4965
{
 
4966
    HRESULT rc;
 
4967
 
 
4968
    try {
 
4969
        ComPtr<IInternalSessionControl> directControl =
 
4970
            mData->mSession.mDirectControl;
 
4971
 
 
4972
        BSTR dummy = NULL; /* will not be changed (setter) */
 
4973
        LONG64 dummy64;
 
4974
        if (!directControl)
 
4975
            rc = E_ACCESSDENIED;
 
4976
        else
 
4977
            rc = directControl->AccessGuestProperty
 
4978
                     (aName,
 
4979
                      /** @todo Fix when adding DeleteGuestProperty(),
 
4980
                                   see defect. */
 
4981
                      *aValue ? aValue : NULL, aFlags, true /* isSetter */,
 
4982
                      &dummy, &dummy64, &dummy);
 
4983
    }
 
4984
    catch (std::bad_alloc &)
 
4985
    {
 
4986
        rc = E_OUTOFMEMORY;
 
4987
    }
 
4988
 
 
4989
    return rc;
 
4990
}
 
4991
#endif // VBOX_WITH_GUEST_PROPS
 
4992
 
 
4993
STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
 
4994
                                       IN_BSTR aFlags)
 
4995
{
 
4996
#ifndef VBOX_WITH_GUEST_PROPS
 
4997
    ReturnComNotImplemented();
 
4998
#else // VBOX_WITH_GUEST_PROPS
 
4999
    CheckComArgStrNotEmptyOrNull(aName);
 
5000
    if ((aFlags != NULL) && !VALID_PTR(aFlags))
 
5001
        return E_INVALIDARG;
 
5002
    AutoCaller autoCaller(this);
 
5003
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5004
 
 
5005
    HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
 
5006
    if (rc == E_ACCESSDENIED)
 
5007
        /* The VM is not running or the service is not (yet) accessible */
 
5008
        rc = setGuestPropertyToService(aName, aValue, aFlags);
 
5009
    return rc;
 
5010
#endif // VBOX_WITH_GUEST_PROPS
 
5011
}
 
5012
 
 
5013
STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
 
5014
{
 
5015
    return SetGuestProperty(aName, aValue, NULL);
 
5016
}
 
5017
 
 
5018
#ifdef VBOX_WITH_GUEST_PROPS
 
5019
/**
 
5020
 * Enumerate the guest properties in VBoxSVC's internal structures.
 
5021
 */
 
5022
HRESULT Machine::enumerateGuestPropertiesInService
 
5023
                (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
 
5024
                 ComSafeArrayOut(BSTR, aValues),
 
5025
                 ComSafeArrayOut(LONG64, aTimestamps),
 
5026
                 ComSafeArrayOut(BSTR, aFlags))
 
5027
{
 
5028
    using namespace guestProp;
 
5029
 
 
5030
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5031
    Utf8Str strPatterns(aPatterns);
 
5032
 
 
5033
    /*
 
5034
     * Look for matching patterns and build up a list.
 
5035
     */
 
5036
    HWData::GuestPropertyList propList;
 
5037
    for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
 
5038
         it != mHWData->mGuestProperties.end();
 
5039
         ++it)
 
5040
        if (   strPatterns.isEmpty()
 
5041
            || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
 
5042
                                            RTSTR_MAX,
 
5043
                                            it->strName.c_str(),
 
5044
                                            RTSTR_MAX,
 
5045
                                            NULL)
 
5046
           )
 
5047
            propList.push_back(*it);
 
5048
 
 
5049
    /*
 
5050
     * And build up the arrays for returning the property information.
 
5051
     */
 
5052
    size_t cEntries = propList.size();
 
5053
    SafeArray<BSTR> names(cEntries);
 
5054
    SafeArray<BSTR> values(cEntries);
 
5055
    SafeArray<LONG64> timestamps(cEntries);
 
5056
    SafeArray<BSTR> flags(cEntries);
 
5057
    size_t iProp = 0;
 
5058
    for (HWData::GuestPropertyList::iterator it = propList.begin();
 
5059
         it != propList.end();
 
5060
         ++it)
 
5061
    {
 
5062
         char szFlags[MAX_FLAGS_LEN + 1];
 
5063
         it->strName.cloneTo(&names[iProp]);
 
5064
         it->strValue.cloneTo(&values[iProp]);
 
5065
         timestamps[iProp] = it->mTimestamp;
 
5066
         writeFlags(it->mFlags, szFlags);
 
5067
         Bstr(szFlags).cloneTo(&flags[iProp]);
 
5068
         ++iProp;
 
5069
    }
 
5070
    names.detachTo(ComSafeArrayOutArg(aNames));
 
5071
    values.detachTo(ComSafeArrayOutArg(aValues));
 
5072
    timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
 
5073
    flags.detachTo(ComSafeArrayOutArg(aFlags));
 
5074
    return S_OK;
 
5075
}
 
5076
 
 
5077
/**
 
5078
 * Enumerate the properties managed by a VM.
 
5079
 * @returns E_ACCESSDENIED if the VM process is not available or not
 
5080
 *          currently handling queries and the setting should then be done in
 
5081
 *          VBoxSVC.
 
5082
 */
 
5083
HRESULT Machine::enumerateGuestPropertiesOnVM
 
5084
                (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
 
5085
                 ComSafeArrayOut(BSTR, aValues),
 
5086
                 ComSafeArrayOut(LONG64, aTimestamps),
 
5087
                 ComSafeArrayOut(BSTR, aFlags))
 
5088
{
 
5089
    HRESULT rc;
 
5090
    ComPtr<IInternalSessionControl> directControl;
 
5091
    directControl = mData->mSession.mDirectControl;
 
5092
 
 
5093
    if (!directControl)
 
5094
        rc = E_ACCESSDENIED;
 
5095
    else
 
5096
        rc = directControl->EnumerateGuestProperties
 
5097
                     (aPatterns, ComSafeArrayOutArg(aNames),
 
5098
                      ComSafeArrayOutArg(aValues),
 
5099
                      ComSafeArrayOutArg(aTimestamps),
 
5100
                      ComSafeArrayOutArg(aFlags));
 
5101
    return rc;
 
5102
}
 
5103
#endif // VBOX_WITH_GUEST_PROPS
 
5104
 
 
5105
STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
 
5106
                                               ComSafeArrayOut(BSTR, aNames),
 
5107
                                               ComSafeArrayOut(BSTR, aValues),
 
5108
                                               ComSafeArrayOut(LONG64, aTimestamps),
 
5109
                                               ComSafeArrayOut(BSTR, aFlags))
 
5110
{
 
5111
#ifndef VBOX_WITH_GUEST_PROPS
 
5112
    ReturnComNotImplemented();
 
5113
#else // VBOX_WITH_GUEST_PROPS
 
5114
    if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
 
5115
        return E_POINTER;
 
5116
 
 
5117
    CheckComArgOutSafeArrayPointerValid(aNames);
 
5118
    CheckComArgOutSafeArrayPointerValid(aValues);
 
5119
    CheckComArgOutSafeArrayPointerValid(aTimestamps);
 
5120
    CheckComArgOutSafeArrayPointerValid(aFlags);
 
5121
 
 
5122
    AutoCaller autoCaller(this);
 
5123
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5124
 
 
5125
    HRESULT rc = enumerateGuestPropertiesOnVM
 
5126
                     (aPatterns, ComSafeArrayOutArg(aNames),
 
5127
                      ComSafeArrayOutArg(aValues),
 
5128
                      ComSafeArrayOutArg(aTimestamps),
 
5129
                      ComSafeArrayOutArg(aFlags));
 
5130
    if (rc == E_ACCESSDENIED)
 
5131
        /* The VM is not running or the service is not (yet) accessible */
 
5132
        rc = enumerateGuestPropertiesInService
 
5133
                     (aPatterns, ComSafeArrayOutArg(aNames),
 
5134
                      ComSafeArrayOutArg(aValues),
 
5135
                      ComSafeArrayOutArg(aTimestamps),
 
5136
                      ComSafeArrayOutArg(aFlags));
 
5137
    return rc;
 
5138
#endif // VBOX_WITH_GUEST_PROPS
 
5139
}
 
5140
 
 
5141
STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
 
5142
                                                       ComSafeArrayOut(IMediumAttachment*, aAttachments))
 
5143
{
 
5144
    MediaData::AttachmentList atts;
 
5145
 
 
5146
    HRESULT rc = getMediumAttachmentsOfController(aName, atts);
 
5147
    if (FAILED(rc)) return rc;
 
5148
 
 
5149
    SafeIfaceArray<IMediumAttachment> attachments(atts);
 
5150
    attachments.detachTo(ComSafeArrayOutArg(aAttachments));
 
5151
 
 
5152
    return S_OK;
 
5153
}
 
5154
 
 
5155
STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
 
5156
                                          LONG aControllerPort,
 
5157
                                          LONG aDevice,
 
5158
                                          IMediumAttachment **aAttachment)
 
5159
{
 
5160
    LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
 
5161
                     aControllerName, aControllerPort, aDevice));
 
5162
 
 
5163
    CheckComArgStrNotEmptyOrNull(aControllerName);
 
5164
    CheckComArgOutPointerValid(aAttachment);
 
5165
 
 
5166
    AutoCaller autoCaller(this);
 
5167
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5168
 
 
5169
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5170
 
 
5171
    *aAttachment = NULL;
 
5172
 
 
5173
    ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
 
5174
                                                         aControllerName,
 
5175
                                                         aControllerPort,
 
5176
                                                         aDevice);
 
5177
    if (pAttach.isNull())
 
5178
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
5179
                        tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
 
5180
                        aDevice, aControllerPort, aControllerName);
 
5181
 
 
5182
    pAttach.queryInterfaceTo(aAttachment);
 
5183
 
 
5184
    return S_OK;
 
5185
}
 
5186
 
 
5187
STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
 
5188
                                           StorageBus_T aConnectionType,
 
5189
                                           IStorageController **controller)
 
5190
{
 
5191
    CheckComArgStrNotEmptyOrNull(aName);
 
5192
 
 
5193
    if (   (aConnectionType <= StorageBus_Null)
 
5194
        || (aConnectionType >  StorageBus_SAS))
 
5195
        return setError(E_INVALIDARG,
 
5196
                        tr("Invalid connection type: %d"),
 
5197
                        aConnectionType);
 
5198
 
 
5199
    AutoCaller autoCaller(this);
 
5200
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5201
 
 
5202
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5203
 
 
5204
    HRESULT rc = checkStateDependency(MutableStateDep);
 
5205
    if (FAILED(rc)) return rc;
 
5206
 
 
5207
    /* try to find one with the name first. */
 
5208
    ComObjPtr<StorageController> ctrl;
 
5209
 
 
5210
    rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
 
5211
    if (SUCCEEDED(rc))
 
5212
        return setError(VBOX_E_OBJECT_IN_USE,
 
5213
                        tr("Storage controller named '%ls' already exists"),
 
5214
                        aName);
 
5215
 
 
5216
    ctrl.createObject();
 
5217
 
 
5218
    /* get a new instance number for the storage controller */
 
5219
    ULONG ulInstance = 0;
 
5220
    bool fBootable = true;
 
5221
    for (StorageControllerList::const_iterator it = mStorageControllers->begin();
 
5222
         it != mStorageControllers->end();
 
5223
         ++it)
 
5224
    {
 
5225
        if ((*it)->getStorageBus() == aConnectionType)
 
5226
        {
 
5227
            ULONG ulCurInst = (*it)->getInstance();
 
5228
 
 
5229
            if (ulCurInst >= ulInstance)
 
5230
                ulInstance = ulCurInst + 1;
 
5231
 
 
5232
            /* Only one controller of each type can be marked as bootable. */
 
5233
            if ((*it)->getBootable())
 
5234
                fBootable = false;
 
5235
        }
 
5236
    }
 
5237
 
 
5238
    rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
 
5239
    if (FAILED(rc)) return rc;
 
5240
 
 
5241
    setModified(IsModified_Storage);
 
5242
    mStorageControllers.backup();
 
5243
    mStorageControllers->push_back(ctrl);
 
5244
 
 
5245
    ctrl.queryInterfaceTo(controller);
 
5246
 
 
5247
    /* inform the direct session if any */
 
5248
    alock.leave();
 
5249
    onStorageControllerChange();
 
5250
 
 
5251
    return S_OK;
 
5252
}
 
5253
 
 
5254
STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
 
5255
                                                 IStorageController **aStorageController)
 
5256
{
 
5257
    CheckComArgStrNotEmptyOrNull(aName);
 
5258
 
 
5259
    AutoCaller autoCaller(this);
 
5260
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5261
 
 
5262
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5263
 
 
5264
    ComObjPtr<StorageController> ctrl;
 
5265
 
 
5266
    HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
 
5267
    if (SUCCEEDED(rc))
 
5268
        ctrl.queryInterfaceTo(aStorageController);
 
5269
 
 
5270
    return rc;
 
5271
}
 
5272
 
 
5273
STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
 
5274
                                                     IStorageController **aStorageController)
 
5275
{
 
5276
    AutoCaller autoCaller(this);
 
5277
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5278
 
 
5279
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5280
 
 
5281
    for (StorageControllerList::const_iterator it = mStorageControllers->begin();
 
5282
         it != mStorageControllers->end();
 
5283
         ++it)
 
5284
    {
 
5285
        if ((*it)->getInstance() == aInstance)
 
5286
        {
 
5287
            (*it).queryInterfaceTo(aStorageController);
 
5288
            return S_OK;
 
5289
        }
 
5290
    }
 
5291
 
 
5292
    return setError(VBOX_E_OBJECT_NOT_FOUND,
 
5293
                    tr("Could not find a storage controller with instance number '%lu'"),
 
5294
                    aInstance);
 
5295
}
 
5296
 
 
5297
STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
 
5298
{
 
5299
    AutoCaller autoCaller(this);
 
5300
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5301
 
 
5302
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5303
 
 
5304
    HRESULT rc = checkStateDependency(MutableStateDep);
 
5305
    if (FAILED(rc)) return rc;
 
5306
 
 
5307
    ComObjPtr<StorageController> ctrl;
 
5308
 
 
5309
    rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
 
5310
    if (SUCCEEDED(rc))
 
5311
    {
 
5312
        /* Ensure that only one controller of each type is marked as bootable. */
 
5313
        if (fBootable == TRUE)
 
5314
        {
 
5315
            for (StorageControllerList::const_iterator it = mStorageControllers->begin();
 
5316
                 it != mStorageControllers->end();
 
5317
                 ++it)
 
5318
            {
 
5319
                ComObjPtr<StorageController> aCtrl = (*it);
 
5320
 
 
5321
                if (   (aCtrl->getName() != Utf8Str(aName))
 
5322
                    && aCtrl->getBootable() == TRUE
 
5323
                    && aCtrl->getStorageBus() == ctrl->getStorageBus()
 
5324
                    && aCtrl->getControllerType() == ctrl->getControllerType())
 
5325
                {
 
5326
                    aCtrl->setBootable(FALSE);
 
5327
                    break;
 
5328
                }
 
5329
            }
 
5330
        }
 
5331
 
 
5332
        if (SUCCEEDED(rc))
 
5333
        {
 
5334
            ctrl->setBootable(fBootable);
 
5335
            setModified(IsModified_Storage);
 
5336
        }
 
5337
    }
 
5338
 
 
5339
    if (SUCCEEDED(rc))
 
5340
    {
 
5341
        /* inform the direct session if any */
 
5342
        alock.leave();
 
5343
        onStorageControllerChange();
 
5344
    }
 
5345
 
 
5346
    return rc;
 
5347
}
 
5348
 
 
5349
STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
 
5350
{
 
5351
    CheckComArgStrNotEmptyOrNull(aName);
 
5352
 
 
5353
    AutoCaller autoCaller(this);
 
5354
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5355
 
 
5356
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5357
 
 
5358
    HRESULT rc = checkStateDependency(MutableStateDep);
 
5359
    if (FAILED(rc)) return rc;
 
5360
 
 
5361
    ComObjPtr<StorageController> ctrl;
 
5362
    rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
 
5363
    if (FAILED(rc)) return rc;
 
5364
 
 
5365
    /* We can remove the controller only if there is no device attached. */
 
5366
    /* check if the device slot is already busy */
 
5367
    for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
 
5368
         it != mMediaData->mAttachments.end();
 
5369
         ++it)
 
5370
    {
 
5371
        if ((*it)->getControllerName() == aName)
 
5372
            return setError(VBOX_E_OBJECT_IN_USE,
 
5373
                            tr("Storage controller named '%ls' has still devices attached"),
 
5374
                            aName);
 
5375
    }
 
5376
 
 
5377
    /* We can remove it now. */
 
5378
    setModified(IsModified_Storage);
 
5379
    mStorageControllers.backup();
 
5380
 
 
5381
    ctrl->unshare();
 
5382
 
 
5383
    mStorageControllers->remove(ctrl);
 
5384
 
 
5385
    /* inform the direct session if any */
 
5386
    alock.leave();
 
5387
    onStorageControllerChange();
 
5388
 
 
5389
    return S_OK;
 
5390
}
 
5391
 
 
5392
STDMETHODIMP Machine::QuerySavedGuestSize(ULONG uScreenId, ULONG *puWidth, ULONG *puHeight)
 
5393
{
 
5394
    LogFlowThisFunc(("\n"));
 
5395
 
 
5396
    CheckComArgNotNull(puWidth);
 
5397
    CheckComArgNotNull(puHeight);
 
5398
 
 
5399
    uint32_t u32Width = 0;
 
5400
    uint32_t u32Height = 0;
 
5401
 
 
5402
    int vrc = readSavedGuestSize(mSSData->mStateFilePath, uScreenId, &u32Width, &u32Height);
 
5403
    if (RT_FAILURE(vrc))
 
5404
        return setError(VBOX_E_IPRT_ERROR,
 
5405
                        tr("Saved guest size is not available (%Rrc)"),
 
5406
                        vrc);
 
5407
 
 
5408
    *puWidth = u32Width;
 
5409
    *puHeight = u32Height;
 
5410
 
 
5411
    return S_OK;
 
5412
}
 
5413
 
 
5414
STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
 
5415
{
 
5416
    LogFlowThisFunc(("\n"));
 
5417
 
 
5418
    CheckComArgNotNull(aSize);
 
5419
    CheckComArgNotNull(aWidth);
 
5420
    CheckComArgNotNull(aHeight);
 
5421
 
 
5422
    if (aScreenId != 0)
 
5423
        return E_NOTIMPL;
 
5424
 
 
5425
    AutoCaller autoCaller(this);
 
5426
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5427
 
 
5428
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5429
 
 
5430
    uint8_t *pu8Data = NULL;
 
5431
    uint32_t cbData = 0;
 
5432
    uint32_t u32Width = 0;
 
5433
    uint32_t u32Height = 0;
 
5434
 
 
5435
    int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
 
5436
 
 
5437
    if (RT_FAILURE(vrc))
 
5438
        return setError(VBOX_E_IPRT_ERROR,
 
5439
                        tr("Saved screenshot data is not available (%Rrc)"),
 
5440
                        vrc);
 
5441
 
 
5442
    *aSize = cbData;
 
5443
    *aWidth = u32Width;
 
5444
    *aHeight = u32Height;
 
5445
 
 
5446
    freeSavedDisplayScreenshot(pu8Data);
 
5447
 
 
5448
    return S_OK;
 
5449
}
 
5450
 
 
5451
STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
 
5452
{
 
5453
    LogFlowThisFunc(("\n"));
 
5454
 
 
5455
    CheckComArgNotNull(aWidth);
 
5456
    CheckComArgNotNull(aHeight);
 
5457
    CheckComArgOutSafeArrayPointerValid(aData);
 
5458
 
 
5459
    if (aScreenId != 0)
 
5460
        return E_NOTIMPL;
 
5461
 
 
5462
    AutoCaller autoCaller(this);
 
5463
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5464
 
 
5465
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5466
 
 
5467
    uint8_t *pu8Data = NULL;
 
5468
    uint32_t cbData = 0;
 
5469
    uint32_t u32Width = 0;
 
5470
    uint32_t u32Height = 0;
 
5471
 
 
5472
    int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
 
5473
 
 
5474
    if (RT_FAILURE(vrc))
 
5475
        return setError(VBOX_E_IPRT_ERROR,
 
5476
                        tr("Saved screenshot data is not available (%Rrc)"),
 
5477
                        vrc);
 
5478
 
 
5479
    *aWidth = u32Width;
 
5480
    *aHeight = u32Height;
 
5481
 
 
5482
    com::SafeArray<BYTE> bitmap(cbData);
 
5483
    /* Convert pixels to format expected by the API caller. */
 
5484
    if (aBGR)
 
5485
    {
 
5486
        /* [0] B, [1] G, [2] R, [3] A. */
 
5487
        for (unsigned i = 0; i < cbData; i += 4)
 
5488
        {
 
5489
            bitmap[i]     = pu8Data[i];
 
5490
            bitmap[i + 1] = pu8Data[i + 1];
 
5491
            bitmap[i + 2] = pu8Data[i + 2];
 
5492
            bitmap[i + 3] = 0xff;
 
5493
        }
 
5494
    }
 
5495
    else
 
5496
    {
 
5497
        /* [0] R, [1] G, [2] B, [3] A. */
 
5498
        for (unsigned i = 0; i < cbData; i += 4)
 
5499
        {
 
5500
            bitmap[i]     = pu8Data[i + 2];
 
5501
            bitmap[i + 1] = pu8Data[i + 1];
 
5502
            bitmap[i + 2] = pu8Data[i];
 
5503
            bitmap[i + 3] = 0xff;
 
5504
        }
 
5505
    }
 
5506
    bitmap.detachTo(ComSafeArrayOutArg(aData));
 
5507
 
 
5508
    freeSavedDisplayScreenshot(pu8Data);
 
5509
 
 
5510
    return S_OK;
 
5511
}
 
5512
 
 
5513
 
 
5514
STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
 
5515
{
 
5516
    LogFlowThisFunc(("\n"));
 
5517
 
 
5518
    CheckComArgNotNull(aWidth);
 
5519
    CheckComArgNotNull(aHeight);
 
5520
    CheckComArgOutSafeArrayPointerValid(aData);
 
5521
 
 
5522
    if (aScreenId != 0)
 
5523
        return E_NOTIMPL;
 
5524
 
 
5525
    AutoCaller autoCaller(this);
 
5526
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5527
 
 
5528
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5529
 
 
5530
    uint8_t *pu8Data = NULL;
 
5531
    uint32_t cbData = 0;
 
5532
    uint32_t u32Width = 0;
 
5533
    uint32_t u32Height = 0;
 
5534
 
 
5535
    int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
 
5536
 
 
5537
    if (RT_FAILURE(vrc))
 
5538
        return setError(VBOX_E_IPRT_ERROR,
 
5539
                        tr("Saved screenshot data is not available (%Rrc)"),
 
5540
                        vrc);
 
5541
 
 
5542
    *aWidth = u32Width;
 
5543
    *aHeight = u32Height;
 
5544
 
 
5545
    uint8_t *pu8PNG = NULL;
 
5546
    uint32_t cbPNG = 0;
 
5547
    uint32_t cxPNG = 0;
 
5548
    uint32_t cyPNG = 0;
 
5549
 
 
5550
    DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
 
5551
 
 
5552
    com::SafeArray<BYTE> screenData(cbPNG);
 
5553
    screenData.initFrom(pu8PNG, cbPNG);
 
5554
    RTMemFree(pu8PNG);
 
5555
 
 
5556
    screenData.detachTo(ComSafeArrayOutArg(aData));
 
5557
 
 
5558
    freeSavedDisplayScreenshot(pu8Data);
 
5559
 
 
5560
    return S_OK;
 
5561
}
 
5562
 
 
5563
STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
 
5564
{
 
5565
    LogFlowThisFunc(("\n"));
 
5566
 
 
5567
    CheckComArgNotNull(aSize);
 
5568
    CheckComArgNotNull(aWidth);
 
5569
    CheckComArgNotNull(aHeight);
 
5570
 
 
5571
    if (aScreenId != 0)
 
5572
        return E_NOTIMPL;
 
5573
 
 
5574
    AutoCaller autoCaller(this);
 
5575
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5576
 
 
5577
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5578
 
 
5579
    uint8_t *pu8Data = NULL;
 
5580
    uint32_t cbData = 0;
 
5581
    uint32_t u32Width = 0;
 
5582
    uint32_t u32Height = 0;
 
5583
 
 
5584
    int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
 
5585
 
 
5586
    if (RT_FAILURE(vrc))
 
5587
        return setError(VBOX_E_IPRT_ERROR,
 
5588
                        tr("Saved screenshot data is not available (%Rrc)"),
 
5589
                        vrc);
 
5590
 
 
5591
    *aSize = cbData;
 
5592
    *aWidth = u32Width;
 
5593
    *aHeight = u32Height;
 
5594
 
 
5595
    freeSavedDisplayScreenshot(pu8Data);
 
5596
 
 
5597
    return S_OK;
 
5598
}
 
5599
 
 
5600
STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
 
5601
{
 
5602
    LogFlowThisFunc(("\n"));
 
5603
 
 
5604
    CheckComArgNotNull(aWidth);
 
5605
    CheckComArgNotNull(aHeight);
 
5606
    CheckComArgOutSafeArrayPointerValid(aData);
 
5607
 
 
5608
    if (aScreenId != 0)
 
5609
        return E_NOTIMPL;
 
5610
 
 
5611
    AutoCaller autoCaller(this);
 
5612
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5613
 
 
5614
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5615
 
 
5616
    uint8_t *pu8Data = NULL;
 
5617
    uint32_t cbData = 0;
 
5618
    uint32_t u32Width = 0;
 
5619
    uint32_t u32Height = 0;
 
5620
 
 
5621
    int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
 
5622
 
 
5623
    if (RT_FAILURE(vrc))
 
5624
        return setError(VBOX_E_IPRT_ERROR,
 
5625
                        tr("Saved screenshot thumbnail data is not available (%Rrc)"),
 
5626
                        vrc);
 
5627
 
 
5628
    *aWidth = u32Width;
 
5629
    *aHeight = u32Height;
 
5630
 
 
5631
    com::SafeArray<BYTE> png(cbData);
 
5632
    png.initFrom(pu8Data, cbData);
 
5633
    png.detachTo(ComSafeArrayOutArg(aData));
 
5634
 
 
5635
    freeSavedDisplayScreenshot(pu8Data);
 
5636
 
 
5637
    return S_OK;
 
5638
}
 
5639
 
 
5640
STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
 
5641
{
 
5642
    HRESULT rc = S_OK;
 
5643
    LogFlowThisFunc(("\n"));
 
5644
 
 
5645
    AutoCaller autoCaller(this);
 
5646
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5647
 
 
5648
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5649
 
 
5650
    if (!mHWData->mCPUHotPlugEnabled)
 
5651
        return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
 
5652
 
 
5653
    if (aCpu >= mHWData->mCPUCount)
 
5654
        return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
 
5655
 
 
5656
    if (mHWData->mCPUAttached[aCpu])
 
5657
        return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
 
5658
 
 
5659
    alock.release();
 
5660
    rc = onCPUChange(aCpu, false);
 
5661
    alock.acquire();
 
5662
    if (FAILED(rc)) return rc;
 
5663
 
 
5664
    setModified(IsModified_MachineData);
 
5665
    mHWData.backup();
 
5666
    mHWData->mCPUAttached[aCpu] = true;
 
5667
 
 
5668
    /* Save settings if online */
 
5669
    if (Global::IsOnline(mData->mMachineState))
 
5670
        saveSettings(NULL);
 
5671
 
 
5672
    return S_OK;
 
5673
}
 
5674
 
 
5675
STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
 
5676
{
 
5677
    HRESULT rc = S_OK;
 
5678
    LogFlowThisFunc(("\n"));
 
5679
 
 
5680
    AutoCaller autoCaller(this);
 
5681
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5682
 
 
5683
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5684
 
 
5685
    if (!mHWData->mCPUHotPlugEnabled)
 
5686
        return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
 
5687
 
 
5688
    if (aCpu >= SchemaDefs::MaxCPUCount)
 
5689
        return setError(E_INVALIDARG,
 
5690
                        tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
 
5691
                        SchemaDefs::MaxCPUCount);
 
5692
 
 
5693
    if (!mHWData->mCPUAttached[aCpu])
 
5694
        return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
 
5695
 
 
5696
    /* CPU 0 can't be detached */
 
5697
    if (aCpu == 0)
 
5698
        return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
 
5699
 
 
5700
    alock.release();
 
5701
    rc = onCPUChange(aCpu, true);
 
5702
    alock.acquire();
 
5703
    if (FAILED(rc)) return rc;
 
5704
 
 
5705
    setModified(IsModified_MachineData);
 
5706
    mHWData.backup();
 
5707
    mHWData->mCPUAttached[aCpu] = false;
 
5708
 
 
5709
    /* Save settings if online */
 
5710
    if (Global::IsOnline(mData->mMachineState))
 
5711
        saveSettings(NULL);
 
5712
 
 
5713
    return S_OK;
 
5714
}
 
5715
 
 
5716
STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
 
5717
{
 
5718
    LogFlowThisFunc(("\n"));
 
5719
 
 
5720
    CheckComArgNotNull(aCpuAttached);
 
5721
 
 
5722
    *aCpuAttached = false;
 
5723
 
 
5724
    AutoCaller autoCaller(this);
 
5725
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5726
 
 
5727
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5728
 
 
5729
    /* If hotplug is enabled the CPU is always enabled. */
 
5730
    if (!mHWData->mCPUHotPlugEnabled)
 
5731
    {
 
5732
        if (aCpu < mHWData->mCPUCount)
 
5733
            *aCpuAttached = true;
 
5734
    }
 
5735
    else
 
5736
    {
 
5737
        if (aCpu < SchemaDefs::MaxCPUCount)
 
5738
            *aCpuAttached = mHWData->mCPUAttached[aCpu];
 
5739
    }
 
5740
 
 
5741
    return S_OK;
 
5742
}
 
5743
 
 
5744
STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
 
5745
{
 
5746
    CheckComArgOutPointerValid(aName);
 
5747
 
 
5748
    AutoCaller autoCaller(this);
 
5749
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5750
 
 
5751
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5752
 
 
5753
    Utf8Str log = queryLogFilename(aIdx);
 
5754
    if (!RTFileExists(log.c_str()))
 
5755
        log.setNull();
 
5756
    log.cloneTo(aName);
 
5757
 
 
5758
    return S_OK;
 
5759
}
 
5760
 
 
5761
STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
 
5762
{
 
5763
    LogFlowThisFunc(("\n"));
 
5764
    CheckComArgOutSafeArrayPointerValid(aData);
 
5765
    if (aSize < 0)
 
5766
        return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
 
5767
 
 
5768
    AutoCaller autoCaller(this);
 
5769
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5770
 
 
5771
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5772
 
 
5773
    HRESULT rc = S_OK;
 
5774
    Utf8Str log = queryLogFilename(aIdx);
 
5775
 
 
5776
    /* do not unnecessarily hold the lock while doing something which does
 
5777
     * not need the lock and potentially takes a long time. */
 
5778
    alock.release();
 
5779
 
 
5780
    /* Limit the chunk size to 32K for now, as that gives better performance
 
5781
     * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
 
5782
     * One byte expands to approx. 25 bytes of breathtaking XML. */
 
5783
    size_t cbData = (size_t)RT_MIN(aSize, 32768);
 
5784
    com::SafeArray<BYTE> logData(cbData);
 
5785
 
 
5786
    RTFILE LogFile;
 
5787
    int vrc = RTFileOpen(&LogFile, log.c_str(),
 
5788
                         RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
 
5789
    if (RT_SUCCESS(vrc))
 
5790
    {
 
5791
        vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
 
5792
        if (RT_SUCCESS(vrc))
 
5793
            logData.resize(cbData);
 
5794
        else
 
5795
            rc = setError(VBOX_E_IPRT_ERROR,
 
5796
                          tr("Could not read log file '%s' (%Rrc)"),
 
5797
                          log.c_str(), vrc);
 
5798
        RTFileClose(LogFile);
 
5799
    }
 
5800
    else
 
5801
        rc = setError(VBOX_E_IPRT_ERROR,
 
5802
                      tr("Could not open log file '%s' (%Rrc)"),
 
5803
                      log.c_str(), vrc);
 
5804
 
 
5805
    if (FAILED(rc))
 
5806
        logData.resize(0);
 
5807
    logData.detachTo(ComSafeArrayOutArg(aData));
 
5808
 
 
5809
    return rc;
 
5810
}
 
5811
 
 
5812
 
 
5813
STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, IEventContext * /*eventContext*/, BOOL /*tryToUnbind*/)
 
5814
{
 
5815
    AutoCaller autoCaller(this);
 
5816
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5817
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5818
 
 
5819
    ComObjPtr<PciDeviceAttachment> pda;
 
5820
    char name[32];
 
5821
 
 
5822
    pda.createObject();
 
5823
    RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
 
5824
    Bstr bname(name);
 
5825
    pda.createObject();
 
5826
    pda->init(this, bname,  hostAddress, desiredGuestAddress, TRUE);
 
5827
 
 
5828
    mPciDeviceAssignments.push_back(pda);
 
5829
    return S_OK;
 
5830
}
 
5831
 
 
5832
STDMETHODIMP Machine::DetachHostPciDevice(LONG /*hostAddress*/)
 
5833
{
 
5834
    AutoCaller autoCaller(this);
 
5835
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5836
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5837
 
 
5838
    return E_NOTIMPL;
 
5839
}
 
5840
 
 
5841
STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
 
5842
{
 
5843
    CheckComArgOutSafeArrayPointerValid(aAssignments);
 
5844
 
 
5845
    AutoCaller autoCaller(this);
 
5846
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5847
 
 
5848
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5849
 
 
5850
    SafeIfaceArray<IPciDeviceAttachment> assignments(mPciDeviceAssignments);
 
5851
    assignments.detachTo(ComSafeArrayOutArg(aAssignments));
 
5852
 
 
5853
    return S_OK;
 
5854
}
 
5855
 
 
5856
STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
 
5857
{
 
5858
    CheckComArgOutPointerValid(aBandwidthControl);
 
5859
 
 
5860
    AutoCaller autoCaller(this);
 
5861
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
5862
 
 
5863
    mBandwidthControl.queryInterfaceTo(aBandwidthControl);
 
5864
 
 
5865
    return S_OK;
 
5866
}
 
5867
 
 
5868
// public methods for internal purposes
 
5869
/////////////////////////////////////////////////////////////////////////////
 
5870
 
 
5871
/**
 
5872
 * Adds the given IsModified_* flag to the dirty flags of the machine.
 
5873
 * This must be called either during loadSettings or under the machine write lock.
 
5874
 * @param fl
 
5875
 */
 
5876
void Machine::setModified(uint32_t fl)
 
5877
{
 
5878
    mData->flModifications |= fl;
 
5879
}
 
5880
 
 
5881
/**
 
5882
 *  Saves the registry entry of this machine to the given configuration node.
 
5883
 *
 
5884
 *  @param aEntryNode Node to save the registry entry to.
 
5885
 *
 
5886
 *  @note locks this object for reading.
 
5887
 */
 
5888
HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
 
5889
{
 
5890
    AutoLimitedCaller autoCaller(this);
 
5891
    AssertComRCReturnRC(autoCaller.rc());
 
5892
 
 
5893
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5894
 
 
5895
    data.uuid = mData->mUuid;
 
5896
    data.strSettingsFile = mData->m_strConfigFile;
 
5897
 
 
5898
    return S_OK;
 
5899
}
 
5900
 
 
5901
/**
 
5902
 * Calculates the absolute path of the given path taking the directory of the
 
5903
 * machine settings file as the current directory.
 
5904
 *
 
5905
 * @param  aPath    Path to calculate the absolute path for.
 
5906
 * @param  aResult  Where to put the result (used only on success, can be the
 
5907
 *                  same Utf8Str instance as passed in @a aPath).
 
5908
 * @return IPRT result.
 
5909
 *
 
5910
 * @note Locks this object for reading.
 
5911
 */
 
5912
int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
 
5913
{
 
5914
    AutoCaller autoCaller(this);
 
5915
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
5916
 
 
5917
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5918
 
 
5919
    AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
 
5920
 
 
5921
    Utf8Str strSettingsDir = mData->m_strConfigFileFull;
 
5922
 
 
5923
    strSettingsDir.stripFilename();
 
5924
    char folder[RTPATH_MAX];
 
5925
    int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
 
5926
    if (RT_SUCCESS(vrc))
 
5927
        aResult = folder;
 
5928
 
 
5929
    return vrc;
 
5930
}
 
5931
 
 
5932
/**
 
5933
 * Copies strSource to strTarget, making it relative to the machine folder
 
5934
 * if it is a subdirectory thereof, or simply copying it otherwise.
 
5935
 *
 
5936
 * @param strSource Path to evaluate and copy.
 
5937
 * @param strTarget Buffer to receive target path.
 
5938
 *
 
5939
 * @note Locks this object for reading.
 
5940
 */
 
5941
void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
 
5942
                                        Utf8Str &strTarget)
 
5943
{
 
5944
    AutoCaller autoCaller(this);
 
5945
    AssertComRCReturn(autoCaller.rc(), (void)0);
 
5946
 
 
5947
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5948
 
 
5949
    AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
 
5950
    // use strTarget as a temporary buffer to hold the machine settings dir
 
5951
    strTarget = mData->m_strConfigFileFull;
 
5952
    strTarget.stripFilename();
 
5953
    if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
 
5954
        // is relative: then append what's left
 
5955
        strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
 
5956
    else
 
5957
        // is not relative: then overwrite
 
5958
        strTarget = strSource;
 
5959
}
 
5960
 
 
5961
/**
 
5962
 *  Returns the full path to the machine's log folder in the
 
5963
 *  \a aLogFolder argument.
 
5964
 */
 
5965
void Machine::getLogFolder(Utf8Str &aLogFolder)
 
5966
{
 
5967
    AutoCaller autoCaller(this);
 
5968
    AssertComRCReturnVoid(autoCaller.rc());
 
5969
 
 
5970
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
5971
 
 
5972
    aLogFolder = mData->m_strConfigFileFull;    // path/to/machinesfolder/vmname/vmname.vbox
 
5973
    aLogFolder.stripFilename();                 // path/to/machinesfolder/vmname
 
5974
    aLogFolder.append(RTPATH_DELIMITER);
 
5975
    aLogFolder.append("Logs");                  // path/to/machinesfolder/vmname/Logs
 
5976
}
 
5977
 
 
5978
/**
 
5979
 *  Returns the full path to the machine's log file for an given index.
 
5980
 */
 
5981
Utf8Str Machine::queryLogFilename(ULONG idx)
 
5982
{
 
5983
    Utf8Str logFolder;
 
5984
    getLogFolder(logFolder);
 
5985
    Assert(logFolder.length());
 
5986
    Utf8Str log;
 
5987
    if (idx == 0)
 
5988
        log = Utf8StrFmt("%s%cVBox.log",
 
5989
                         logFolder.c_str(), RTPATH_DELIMITER);
 
5990
    else
 
5991
        log = Utf8StrFmt("%s%cVBox.log.%d",
 
5992
                         logFolder.c_str(), RTPATH_DELIMITER, idx);
 
5993
    return log;
 
5994
}
 
5995
 
 
5996
/**
 
5997
 *  @note Locks this object for writing, calls the client process
 
5998
 *        (inside the lock).
 
5999
 */
 
6000
HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
 
6001
                                   IN_BSTR aType,
 
6002
                                   IN_BSTR aEnvironment,
 
6003
                                   ProgressProxy *aProgress)
 
6004
{
 
6005
    LogFlowThisFuncEnter();
 
6006
 
 
6007
    AssertReturn(aControl, E_FAIL);
 
6008
    AssertReturn(aProgress, E_FAIL);
 
6009
 
 
6010
    AutoCaller autoCaller(this);
 
6011
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
6012
 
 
6013
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6014
 
 
6015
    if (!mData->mRegistered)
 
6016
        return setError(E_UNEXPECTED,
 
6017
                        tr("The machine '%s' is not registered"),
 
6018
                        mUserData->s.strName.c_str());
 
6019
 
 
6020
    LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
 
6021
 
 
6022
    if (    mData->mSession.mState == SessionState_Locked
 
6023
         || mData->mSession.mState == SessionState_Spawning
 
6024
         || mData->mSession.mState == SessionState_Unlocking)
 
6025
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
6026
                        tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
 
6027
                        mUserData->s.strName.c_str());
 
6028
 
 
6029
    /* may not be busy */
 
6030
    AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
 
6031
 
 
6032
    /* get the path to the executable */
 
6033
    char szPath[RTPATH_MAX];
 
6034
    RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
 
6035
    size_t sz = strlen(szPath);
 
6036
    szPath[sz++] = RTPATH_DELIMITER;
 
6037
    szPath[sz] = 0;
 
6038
    char *cmd = szPath + sz;
 
6039
    sz = RTPATH_MAX - sz;
 
6040
 
 
6041
    int vrc = VINF_SUCCESS;
 
6042
    RTPROCESS pid = NIL_RTPROCESS;
 
6043
 
 
6044
    RTENV env = RTENV_DEFAULT;
 
6045
 
 
6046
    if (aEnvironment != NULL && *aEnvironment)
 
6047
    {
 
6048
        char *newEnvStr = NULL;
 
6049
 
 
6050
        do
 
6051
        {
 
6052
            /* clone the current environment */
 
6053
            int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
 
6054
            AssertRCBreakStmt(vrc2, vrc = vrc2);
 
6055
 
 
6056
            newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
 
6057
            AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
 
6058
 
 
6059
            /* put new variables to the environment
 
6060
             * (ignore empty variable names here since RTEnv API
 
6061
             * intentionally doesn't do that) */
 
6062
            char *var = newEnvStr;
 
6063
            for (char *p = newEnvStr; *p; ++p)
 
6064
            {
 
6065
                if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
 
6066
                {
 
6067
                    *p = '\0';
 
6068
                    if (*var)
 
6069
                    {
 
6070
                        char *val = strchr(var, '=');
 
6071
                        if (val)
 
6072
                        {
 
6073
                            *val++ = '\0';
 
6074
                            vrc2 = RTEnvSetEx(env, var, val);
 
6075
                        }
 
6076
                        else
 
6077
                            vrc2 = RTEnvUnsetEx(env, var);
 
6078
                        if (RT_FAILURE(vrc2))
 
6079
                            break;
 
6080
                    }
 
6081
                    var = p + 1;
 
6082
                }
 
6083
            }
 
6084
            if (RT_SUCCESS(vrc2) && *var)
 
6085
                vrc2 = RTEnvPutEx(env, var);
 
6086
 
 
6087
            AssertRCBreakStmt(vrc2, vrc = vrc2);
 
6088
        }
 
6089
        while (0);
 
6090
 
 
6091
        if (newEnvStr != NULL)
 
6092
            RTStrFree(newEnvStr);
 
6093
    }
 
6094
 
 
6095
    Utf8Str strType(aType);
 
6096
 
 
6097
    /* Qt is default */
 
6098
#ifdef VBOX_WITH_QTGUI
 
6099
    if (strType == "gui" || strType == "GUI/Qt")
 
6100
    {
 
6101
# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
 
6102
        const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
 
6103
# else
 
6104
        const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
 
6105
# endif
 
6106
        Assert(sz >= sizeof(VirtualBox_exe));
 
6107
        strcpy(cmd, VirtualBox_exe);
 
6108
 
 
6109
        Utf8Str idStr = mData->mUuid.toString();
 
6110
        const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
 
6111
        vrc = RTProcCreate(szPath, args, env, 0, &pid);
 
6112
    }
 
6113
#else /* !VBOX_WITH_QTGUI */
 
6114
    if (0)
 
6115
        ;
 
6116
#endif /* VBOX_WITH_QTGUI */
 
6117
 
 
6118
    else
 
6119
 
 
6120
#ifdef VBOX_WITH_VBOXSDL
 
6121
    if (strType == "sdl" || strType == "GUI/SDL")
 
6122
    {
 
6123
        const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
 
6124
        Assert(sz >= sizeof(VBoxSDL_exe));
 
6125
        strcpy(cmd, VBoxSDL_exe);
 
6126
 
 
6127
        Utf8Str idStr = mData->mUuid.toString();
 
6128
        const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
 
6129
        vrc = RTProcCreate(szPath, args, env, 0, &pid);
 
6130
    }
 
6131
#else /* !VBOX_WITH_VBOXSDL */
 
6132
    if (0)
 
6133
        ;
 
6134
#endif /* !VBOX_WITH_VBOXSDL */
 
6135
 
 
6136
    else
 
6137
 
 
6138
#ifdef VBOX_WITH_HEADLESS
 
6139
    if (   strType == "headless"
 
6140
        || strType == "capture"
 
6141
        || strType == "vrdp" /* Deprecated. Same as headless. */
 
6142
       )
 
6143
    {
 
6144
        /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
 
6145
         * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
 
6146
         * and a VM works even if the server has not been installed.
 
6147
         * So in 4.0 the "headless" behavior remains the same for default VBox installations.
 
6148
         * Only if a VRDE has been installed and the VM enables it, the "headless" will work
 
6149
         * differently in 4.0 and 3.x.
 
6150
         */
 
6151
        const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
 
6152
        Assert(sz >= sizeof(VBoxHeadless_exe));
 
6153
        strcpy(cmd, VBoxHeadless_exe);
 
6154
 
 
6155
        Utf8Str idStr = mData->mUuid.toString();
 
6156
        /* Leave space for "--capture" arg. */
 
6157
        const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0, 0 };
 
6158
        if (strType == "capture")
 
6159
        {
 
6160
            unsigned pos = RT_ELEMENTS(args) - 2;
 
6161
            args[pos] = "--capture";
 
6162
        }
 
6163
        vrc = RTProcCreate(szPath, args, env, 0, &pid);
 
6164
    }
 
6165
#else /* !VBOX_WITH_HEADLESS */
 
6166
    if (0)
 
6167
        ;
 
6168
#endif /* !VBOX_WITH_HEADLESS */
 
6169
    else
 
6170
    {
 
6171
        RTEnvDestroy(env);
 
6172
        return setError(E_INVALIDARG,
 
6173
                        tr("Invalid session type: '%s'"),
 
6174
                        strType.c_str());
 
6175
    }
 
6176
 
 
6177
    RTEnvDestroy(env);
 
6178
 
 
6179
    if (RT_FAILURE(vrc))
 
6180
        return setError(VBOX_E_IPRT_ERROR,
 
6181
                        tr("Could not launch a process for the machine '%s' (%Rrc)"),
 
6182
                        mUserData->s.strName.c_str(), vrc);
 
6183
 
 
6184
    LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
 
6185
 
 
6186
    /*
 
6187
     *  Note that we don't leave the lock here before calling the client,
 
6188
     *  because it doesn't need to call us back if called with a NULL argument.
 
6189
     *  Leaving the lock here is dangerous because we didn't prepare the
 
6190
     *  launch data yet, but the client we've just started may happen to be
 
6191
     *  too fast and call openSession() that will fail (because of PID, etc.),
 
6192
     *  so that the Machine will never get out of the Spawning session state.
 
6193
     */
 
6194
 
 
6195
    /* inform the session that it will be a remote one */
 
6196
    LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
 
6197
    HRESULT rc = aControl->AssignMachine(NULL);
 
6198
    LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
 
6199
 
 
6200
    if (FAILED(rc))
 
6201
    {
 
6202
        /* restore the session state */
 
6203
        mData->mSession.mState = SessionState_Unlocked;
 
6204
        /* The failure may occur w/o any error info (from RPC), so provide one */
 
6205
        return setError(VBOX_E_VM_ERROR,
 
6206
                        tr("Failed to assign the machine to the session (%Rrc)"), rc);
 
6207
    }
 
6208
 
 
6209
    /* attach launch data to the machine */
 
6210
    Assert(mData->mSession.mPid == NIL_RTPROCESS);
 
6211
    mData->mSession.mRemoteControls.push_back (aControl);
 
6212
    mData->mSession.mProgress = aProgress;
 
6213
    mData->mSession.mPid = pid;
 
6214
    mData->mSession.mState = SessionState_Spawning;
 
6215
    mData->mSession.mType = strType;
 
6216
 
 
6217
    LogFlowThisFuncLeave();
 
6218
    return S_OK;
 
6219
}
 
6220
 
 
6221
/**
 
6222
 * Returns @c true if the given machine has an open direct session and returns
 
6223
 * the session machine instance and additional session data (on some platforms)
 
6224
 * if so.
 
6225
 *
 
6226
 * Note that when the method returns @c false, the arguments remain unchanged.
 
6227
 *
 
6228
 * @param aMachine  Session machine object.
 
6229
 * @param aControl  Direct session control object (optional).
 
6230
 * @param aIPCSem   Mutex IPC semaphore handle for this machine (optional).
 
6231
 *
 
6232
 * @note locks this object for reading.
 
6233
 */
 
6234
#if defined(RT_OS_WINDOWS)
 
6235
bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
 
6236
                            ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
 
6237
                            HANDLE *aIPCSem /*= NULL*/,
 
6238
                            bool aAllowClosing /*= false*/)
 
6239
#elif defined(RT_OS_OS2)
 
6240
bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
 
6241
                            ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
 
6242
                            HMTX *aIPCSem /*= NULL*/,
 
6243
                            bool aAllowClosing /*= false*/)
 
6244
#else
 
6245
bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
 
6246
                            ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
 
6247
                            bool aAllowClosing /*= false*/)
 
6248
#endif
 
6249
{
 
6250
    AutoLimitedCaller autoCaller(this);
 
6251
    AssertComRCReturn(autoCaller.rc(), false);
 
6252
 
 
6253
    /* just return false for inaccessible machines */
 
6254
    if (autoCaller.state() != Ready)
 
6255
        return false;
 
6256
 
 
6257
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6258
 
 
6259
    if (    mData->mSession.mState == SessionState_Locked
 
6260
         || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
 
6261
       )
 
6262
    {
 
6263
        AssertReturn(!mData->mSession.mMachine.isNull(), false);
 
6264
 
 
6265
        aMachine = mData->mSession.mMachine;
 
6266
 
 
6267
        if (aControl != NULL)
 
6268
            *aControl = mData->mSession.mDirectControl;
 
6269
 
 
6270
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
 
6271
        /* Additional session data */
 
6272
        if (aIPCSem != NULL)
 
6273
            *aIPCSem = aMachine->mIPCSem;
 
6274
#endif
 
6275
        return true;
 
6276
    }
 
6277
 
 
6278
    return false;
 
6279
}
 
6280
 
 
6281
/**
 
6282
 * Returns @c true if the given machine has an spawning direct session and
 
6283
 * returns and additional session data (on some platforms) if so.
 
6284
 *
 
6285
 * Note that when the method returns @c false, the arguments remain unchanged.
 
6286
 *
 
6287
 * @param aPID  PID of the spawned direct session process.
 
6288
 *
 
6289
 * @note locks this object for reading.
 
6290
 */
 
6291
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
 
6292
bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
 
6293
#else
 
6294
bool Machine::isSessionSpawning()
 
6295
#endif
 
6296
{
 
6297
    AutoLimitedCaller autoCaller(this);
 
6298
    AssertComRCReturn(autoCaller.rc(), false);
 
6299
 
 
6300
    /* just return false for inaccessible machines */
 
6301
    if (autoCaller.state() != Ready)
 
6302
        return false;
 
6303
 
 
6304
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6305
 
 
6306
    if (mData->mSession.mState == SessionState_Spawning)
 
6307
    {
 
6308
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
 
6309
        /* Additional session data */
 
6310
        if (aPID != NULL)
 
6311
        {
 
6312
            AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
 
6313
            *aPID = mData->mSession.mPid;
 
6314
        }
 
6315
#endif
 
6316
        return true;
 
6317
    }
 
6318
 
 
6319
    return false;
 
6320
}
 
6321
 
 
6322
/**
 
6323
 * Called from the client watcher thread to check for unexpected client process
 
6324
 * death during Session_Spawning state (e.g. before it successfully opened a
 
6325
 * direct session).
 
6326
 *
 
6327
 * On Win32 and on OS/2, this method is called only when we've got the
 
6328
 * direct client's process termination notification, so it always returns @c
 
6329
 * true.
 
6330
 *
 
6331
 * On other platforms, this method returns @c true if the client process is
 
6332
 * terminated and @c false if it's still alive.
 
6333
 *
 
6334
 * @note Locks this object for writing.
 
6335
 */
 
6336
bool Machine::checkForSpawnFailure()
 
6337
{
 
6338
    AutoCaller autoCaller(this);
 
6339
    if (!autoCaller.isOk())
 
6340
    {
 
6341
        /* nothing to do */
 
6342
        LogFlowThisFunc(("Already uninitialized!\n"));
 
6343
        return true;
 
6344
    }
 
6345
 
 
6346
    /* VirtualBox::addProcessToReap() needs a write lock */
 
6347
    AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
 
6348
 
 
6349
    if (mData->mSession.mState != SessionState_Spawning)
 
6350
    {
 
6351
        /* nothing to do */
 
6352
        LogFlowThisFunc(("Not spawning any more!\n"));
 
6353
        return true;
 
6354
    }
 
6355
 
 
6356
    HRESULT rc = S_OK;
 
6357
 
 
6358
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
 
6359
 
 
6360
    /* the process was already unexpectedly terminated, we just need to set an
 
6361
     * error and finalize session spawning */
 
6362
    rc = setError(E_FAIL,
 
6363
                  tr("The virtual machine '%s' has terminated unexpectedly during startup"),
 
6364
                  getName().c_str());
 
6365
#else
 
6366
 
 
6367
    /* PID not yet initialized, skip check. */
 
6368
    if (mData->mSession.mPid == NIL_RTPROCESS)
 
6369
        return false;
 
6370
 
 
6371
    RTPROCSTATUS status;
 
6372
    int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
 
6373
                           &status);
 
6374
 
 
6375
    if (vrc != VERR_PROCESS_RUNNING)
 
6376
    {
 
6377
        if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
 
6378
            rc = setError(E_FAIL,
 
6379
                          tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
 
6380
                          getName().c_str(), status.iStatus);
 
6381
        else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
 
6382
            rc = setError(E_FAIL,
 
6383
                          tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
 
6384
                          getName().c_str(), status.iStatus);
 
6385
        else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
 
6386
            rc = setError(E_FAIL,
 
6387
                          tr("The virtual machine '%s' has terminated abnormally"),
 
6388
                          getName().c_str(), status.iStatus);
 
6389
        else
 
6390
            rc = setError(E_FAIL,
 
6391
                          tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
 
6392
                          getName().c_str(), rc);
 
6393
    }
 
6394
 
 
6395
#endif
 
6396
 
 
6397
    if (FAILED(rc))
 
6398
    {
 
6399
        /* Close the remote session, remove the remote control from the list
 
6400
         * and reset session state to Closed (@note keep the code in sync with
 
6401
         * the relevant part in checkForSpawnFailure()). */
 
6402
 
 
6403
        Assert(mData->mSession.mRemoteControls.size() == 1);
 
6404
        if (mData->mSession.mRemoteControls.size() == 1)
 
6405
        {
 
6406
            ErrorInfoKeeper eik;
 
6407
            mData->mSession.mRemoteControls.front()->Uninitialize();
 
6408
        }
 
6409
 
 
6410
        mData->mSession.mRemoteControls.clear();
 
6411
        mData->mSession.mState = SessionState_Unlocked;
 
6412
 
 
6413
        /* finalize the progress after setting the state */
 
6414
        if (!mData->mSession.mProgress.isNull())
 
6415
        {
 
6416
            mData->mSession.mProgress->notifyComplete(rc);
 
6417
            mData->mSession.mProgress.setNull();
 
6418
        }
 
6419
 
 
6420
        mParent->addProcessToReap(mData->mSession.mPid);
 
6421
        mData->mSession.mPid = NIL_RTPROCESS;
 
6422
 
 
6423
        mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
 
6424
        return true;
 
6425
    }
 
6426
 
 
6427
    return false;
 
6428
}
 
6429
 
 
6430
/**
 
6431
 *  Checks whether the machine can be registered. If so, commits and saves
 
6432
 *  all settings.
 
6433
 *
 
6434
 *  @note Must be called from mParent's write lock. Locks this object and
 
6435
 *  children for writing.
 
6436
 */
 
6437
HRESULT Machine::prepareRegister()
 
6438
{
 
6439
    AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
 
6440
 
 
6441
    AutoLimitedCaller autoCaller(this);
 
6442
    AssertComRCReturnRC(autoCaller.rc());
 
6443
 
 
6444
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6445
 
 
6446
    /* wait for state dependents to drop to zero */
 
6447
    ensureNoStateDependencies();
 
6448
 
 
6449
    if (!mData->mAccessible)
 
6450
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
6451
                        tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
 
6452
                        mUserData->s.strName.c_str(),
 
6453
                        mData->mUuid.toString().c_str());
 
6454
 
 
6455
    AssertReturn(autoCaller.state() == Ready, E_FAIL);
 
6456
 
 
6457
    if (mData->mRegistered)
 
6458
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
6459
                        tr("The machine '%s' with UUID {%s} is already registered"),
 
6460
                        mUserData->s.strName.c_str(),
 
6461
                        mData->mUuid.toString().c_str());
 
6462
 
 
6463
    HRESULT rc = S_OK;
 
6464
 
 
6465
    // Ensure the settings are saved. If we are going to be registered and
 
6466
    // no config file exists yet, create it by calling saveSettings() too.
 
6467
    if (    (mData->flModifications)
 
6468
         || (!mData->pMachineConfigFile->fileExists())
 
6469
       )
 
6470
    {
 
6471
        rc = saveSettings(NULL);
 
6472
                // no need to check whether VirtualBox.xml needs saving too since
 
6473
                // we can't have a machine XML file rename pending
 
6474
        if (FAILED(rc)) return rc;
 
6475
    }
 
6476
 
 
6477
    /* more config checking goes here */
 
6478
 
 
6479
    if (SUCCEEDED(rc))
 
6480
    {
 
6481
        /* we may have had implicit modifications we want to fix on success */
 
6482
        commit();
 
6483
 
 
6484
        mData->mRegistered = true;
 
6485
    }
 
6486
    else
 
6487
    {
 
6488
        /* we may have had implicit modifications we want to cancel on failure*/
 
6489
        rollback(false /* aNotify */);
 
6490
    }
 
6491
 
 
6492
    return rc;
 
6493
}
 
6494
 
 
6495
/**
 
6496
 * Increases the number of objects dependent on the machine state or on the
 
6497
 * registered state. Guarantees that these two states will not change at least
 
6498
 * until #releaseStateDependency() is called.
 
6499
 *
 
6500
 * Depending on the @a aDepType value, additional state checks may be made.
 
6501
 * These checks will set extended error info on failure. See
 
6502
 * #checkStateDependency() for more info.
 
6503
 *
 
6504
 * If this method returns a failure, the dependency is not added and the caller
 
6505
 * is not allowed to rely on any particular machine state or registration state
 
6506
 * value and may return the failed result code to the upper level.
 
6507
 *
 
6508
 * @param aDepType      Dependency type to add.
 
6509
 * @param aState        Current machine state (NULL if not interested).
 
6510
 * @param aRegistered   Current registered state (NULL if not interested).
 
6511
 *
 
6512
 * @note Locks this object for writing.
 
6513
 */
 
6514
HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
 
6515
                                    MachineState_T *aState /* = NULL */,
 
6516
                                    BOOL *aRegistered /* = NULL */)
 
6517
{
 
6518
    AutoCaller autoCaller(this);
 
6519
    AssertComRCReturnRC(autoCaller.rc());
 
6520
 
 
6521
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6522
 
 
6523
    HRESULT rc = checkStateDependency(aDepType);
 
6524
    if (FAILED(rc)) return rc;
 
6525
 
 
6526
    {
 
6527
        if (mData->mMachineStateChangePending != 0)
 
6528
        {
 
6529
            /* ensureNoStateDependencies() is waiting for state dependencies to
 
6530
             * drop to zero so don't add more. It may make sense to wait a bit
 
6531
             * and retry before reporting an error (since the pending state
 
6532
             * transition should be really quick) but let's just assert for
 
6533
             * now to see if it ever happens on practice. */
 
6534
 
 
6535
            AssertFailed();
 
6536
 
 
6537
            return setError(E_ACCESSDENIED,
 
6538
                            tr("Machine state change is in progress. Please retry the operation later."));
 
6539
        }
 
6540
 
 
6541
        ++mData->mMachineStateDeps;
 
6542
        Assert(mData->mMachineStateDeps != 0 /* overflow */);
 
6543
    }
 
6544
 
 
6545
    if (aState)
 
6546
        *aState = mData->mMachineState;
 
6547
    if (aRegistered)
 
6548
        *aRegistered = mData->mRegistered;
 
6549
 
 
6550
    return S_OK;
 
6551
}
 
6552
 
 
6553
/**
 
6554
 * Decreases the number of objects dependent on the machine state.
 
6555
 * Must always complete the #addStateDependency() call after the state
 
6556
 * dependency is no more necessary.
 
6557
 */
 
6558
void Machine::releaseStateDependency()
 
6559
{
 
6560
    AutoCaller autoCaller(this);
 
6561
    AssertComRCReturnVoid(autoCaller.rc());
 
6562
 
 
6563
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6564
 
 
6565
    /* releaseStateDependency() w/o addStateDependency()? */
 
6566
    AssertReturnVoid(mData->mMachineStateDeps != 0);
 
6567
    -- mData->mMachineStateDeps;
 
6568
 
 
6569
    if (mData->mMachineStateDeps == 0)
 
6570
    {
 
6571
        /* inform ensureNoStateDependencies() that there are no more deps */
 
6572
        if (mData->mMachineStateChangePending != 0)
 
6573
        {
 
6574
            Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
 
6575
            RTSemEventMultiSignal (mData->mMachineStateDepsSem);
 
6576
        }
 
6577
    }
 
6578
}
 
6579
 
 
6580
// protected methods
 
6581
/////////////////////////////////////////////////////////////////////////////
 
6582
 
 
6583
/**
 
6584
 *  Performs machine state checks based on the @a aDepType value. If a check
 
6585
 *  fails, this method will set extended error info, otherwise it will return
 
6586
 *  S_OK. It is supposed, that on failure, the caller will immediately return
 
6587
 *  the return value of this method to the upper level.
 
6588
 *
 
6589
 *  When @a aDepType is AnyStateDep, this method always returns S_OK.
 
6590
 *
 
6591
 *  When @a aDepType is MutableStateDep, this method returns S_OK only if the
 
6592
 *  current state of this machine object allows to change settings of the
 
6593
 *  machine (i.e. the machine is not registered, or registered but not running
 
6594
 *  and not saved). It is useful to call this method from Machine setters
 
6595
 *  before performing any change.
 
6596
 *
 
6597
 *  When @a aDepType is MutableOrSavedStateDep, this method behaves the same
 
6598
 *  as for MutableStateDep except that if the machine is saved, S_OK is also
 
6599
 *  returned. This is useful in setters which allow changing machine
 
6600
 *  properties when it is in the saved state.
 
6601
 *
 
6602
 *  @param aDepType     Dependency type to check.
 
6603
 *
 
6604
 *  @note Non Machine based classes should use #addStateDependency() and
 
6605
 *  #releaseStateDependency() methods or the smart AutoStateDependency
 
6606
 *  template.
 
6607
 *
 
6608
 *  @note This method must be called from under this object's read or write
 
6609
 *        lock.
 
6610
 */
 
6611
HRESULT Machine::checkStateDependency(StateDependency aDepType)
 
6612
{
 
6613
    switch (aDepType)
 
6614
    {
 
6615
        case AnyStateDep:
 
6616
        {
 
6617
            break;
 
6618
        }
 
6619
        case MutableStateDep:
 
6620
        {
 
6621
            if (   mData->mRegistered
 
6622
                && (   !isSessionMachine()  /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
 
6623
                    || (   mData->mMachineState != MachineState_Paused
 
6624
                        && mData->mMachineState != MachineState_Running
 
6625
                        && mData->mMachineState != MachineState_Aborted
 
6626
                        && mData->mMachineState != MachineState_Teleported
 
6627
                        && mData->mMachineState != MachineState_PoweredOff
 
6628
                       )
 
6629
                   )
 
6630
               )
 
6631
                return setError(VBOX_E_INVALID_VM_STATE,
 
6632
                                tr("The machine is not mutable (state is %s)"),
 
6633
                                Global::stringifyMachineState(mData->mMachineState));
 
6634
            break;
 
6635
        }
 
6636
        case MutableOrSavedStateDep:
 
6637
        {
 
6638
            if (   mData->mRegistered
 
6639
                && (   !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
 
6640
                    || (   mData->mMachineState != MachineState_Paused
 
6641
                        && mData->mMachineState != MachineState_Running
 
6642
                        && mData->mMachineState != MachineState_Aborted
 
6643
                        && mData->mMachineState != MachineState_Teleported
 
6644
                        && mData->mMachineState != MachineState_Saved
 
6645
                        && mData->mMachineState != MachineState_PoweredOff
 
6646
                       )
 
6647
                   )
 
6648
               )
 
6649
                return setError(VBOX_E_INVALID_VM_STATE,
 
6650
                                tr("The machine is not mutable (state is %s)"),
 
6651
                                Global::stringifyMachineState(mData->mMachineState));
 
6652
            break;
 
6653
        }
 
6654
    }
 
6655
 
 
6656
    return S_OK;
 
6657
}
 
6658
 
 
6659
/**
 
6660
 * Helper to initialize all associated child objects and allocate data
 
6661
 * structures.
 
6662
 *
 
6663
 * This method must be called as a part of the object's initialization procedure
 
6664
 * (usually done in the #init() method).
 
6665
 *
 
6666
 * @note Must be called only from #init() or from #registeredInit().
 
6667
 */
 
6668
HRESULT Machine::initDataAndChildObjects()
 
6669
{
 
6670
    AutoCaller autoCaller(this);
 
6671
    AssertComRCReturnRC(autoCaller.rc());
 
6672
    AssertComRCReturn(autoCaller.state() == InInit ||
 
6673
                      autoCaller.state() == Limited, E_FAIL);
 
6674
 
 
6675
    AssertReturn(!mData->mAccessible, E_FAIL);
 
6676
 
 
6677
    /* allocate data structures */
 
6678
    mSSData.allocate();
 
6679
    mUserData.allocate();
 
6680
    mHWData.allocate();
 
6681
    mMediaData.allocate();
 
6682
    mStorageControllers.allocate();
 
6683
 
 
6684
    /* initialize mOSTypeId */
 
6685
    mUserData->s.strOsType = mParent->getUnknownOSType()->id();
 
6686
 
 
6687
    /* create associated BIOS settings object */
 
6688
    unconst(mBIOSSettings).createObject();
 
6689
    mBIOSSettings->init(this);
 
6690
 
 
6691
    /* create an associated VRDE object (default is disabled) */
 
6692
    unconst(mVRDEServer).createObject();
 
6693
    mVRDEServer->init(this);
 
6694
 
 
6695
    /* create associated serial port objects */
 
6696
    for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
 
6697
    {
 
6698
        unconst(mSerialPorts[slot]).createObject();
 
6699
        mSerialPorts[slot]->init(this, slot);
 
6700
    }
 
6701
 
 
6702
    /* create associated parallel port objects */
 
6703
    for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
 
6704
    {
 
6705
        unconst(mParallelPorts[slot]).createObject();
 
6706
        mParallelPorts[slot]->init(this, slot);
 
6707
    }
 
6708
 
 
6709
    /* create the audio adapter object (always present, default is disabled) */
 
6710
    unconst(mAudioAdapter).createObject();
 
6711
    mAudioAdapter->init(this);
 
6712
 
 
6713
    /* create the USB controller object (always present, default is disabled) */
 
6714
    unconst(mUSBController).createObject();
 
6715
    mUSBController->init(this);
 
6716
 
 
6717
    /* create associated network adapter objects */
 
6718
    for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot ++)
 
6719
    {
 
6720
        unconst(mNetworkAdapters[slot]).createObject();
 
6721
        mNetworkAdapters[slot]->init(this, slot);
 
6722
    }
 
6723
 
 
6724
    /* create the bandwidth control */
 
6725
    unconst(mBandwidthControl).createObject();
 
6726
    mBandwidthControl->init(this);
 
6727
 
 
6728
    return S_OK;
 
6729
}
 
6730
 
 
6731
/**
 
6732
 * Helper to uninitialize all associated child objects and to free all data
 
6733
 * structures.
 
6734
 *
 
6735
 * This method must be called as a part of the object's uninitialization
 
6736
 * procedure (usually done in the #uninit() method).
 
6737
 *
 
6738
 * @note Must be called only from #uninit() or from #registeredInit().
 
6739
 */
 
6740
void Machine::uninitDataAndChildObjects()
 
6741
{
 
6742
    AutoCaller autoCaller(this);
 
6743
    AssertComRCReturnVoid(autoCaller.rc());
 
6744
    AssertComRCReturnVoid(    autoCaller.state() == InUninit
 
6745
                           || autoCaller.state() == Limited);
 
6746
 
 
6747
    /* tell all our other child objects we've been uninitialized */
 
6748
    if (mBandwidthControl)
 
6749
    {
 
6750
        mBandwidthControl->uninit();
 
6751
        unconst(mBandwidthControl).setNull();
 
6752
    }
 
6753
 
 
6754
    for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
 
6755
    {
 
6756
        if (mNetworkAdapters[slot])
 
6757
        {
 
6758
            mNetworkAdapters[slot]->uninit();
 
6759
            unconst(mNetworkAdapters[slot]).setNull();
 
6760
        }
 
6761
    }
 
6762
 
 
6763
    if (mUSBController)
 
6764
    {
 
6765
        mUSBController->uninit();
 
6766
        unconst(mUSBController).setNull();
 
6767
    }
 
6768
 
 
6769
    if (mAudioAdapter)
 
6770
    {
 
6771
        mAudioAdapter->uninit();
 
6772
        unconst(mAudioAdapter).setNull();
 
6773
    }
 
6774
 
 
6775
    for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
 
6776
    {
 
6777
        if (mParallelPorts[slot])
 
6778
        {
 
6779
            mParallelPorts[slot]->uninit();
 
6780
            unconst(mParallelPorts[slot]).setNull();
 
6781
        }
 
6782
    }
 
6783
 
 
6784
    for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
 
6785
    {
 
6786
        if (mSerialPorts[slot])
 
6787
        {
 
6788
            mSerialPorts[slot]->uninit();
 
6789
            unconst(mSerialPorts[slot]).setNull();
 
6790
        }
 
6791
    }
 
6792
 
 
6793
    if (mVRDEServer)
 
6794
    {
 
6795
        mVRDEServer->uninit();
 
6796
        unconst(mVRDEServer).setNull();
 
6797
    }
 
6798
 
 
6799
    if (mBIOSSettings)
 
6800
    {
 
6801
        mBIOSSettings->uninit();
 
6802
        unconst(mBIOSSettings).setNull();
 
6803
    }
 
6804
 
 
6805
    /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
 
6806
     * instance is uninitialized; SessionMachine instances refer to real
 
6807
     * Machine hard disks). This is necessary for a clean re-initialization of
 
6808
     * the VM after successfully re-checking the accessibility state. Note
 
6809
     * that in case of normal Machine or SnapshotMachine uninitialization (as
 
6810
     * a result of unregistering or deleting the snapshot), outdated hard
 
6811
     * disk attachments will already be uninitialized and deleted, so this
 
6812
     * code will not affect them. */
 
6813
    if (    !!mMediaData
 
6814
         && (!isSessionMachine())
 
6815
       )
 
6816
    {
 
6817
        for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
 
6818
             it != mMediaData->mAttachments.end();
 
6819
             ++it)
 
6820
        {
 
6821
            ComObjPtr<Medium> hd = (*it)->getMedium();
 
6822
            if (hd.isNull())
 
6823
                continue;
 
6824
            HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
 
6825
            AssertComRC(rc);
 
6826
        }
 
6827
    }
 
6828
 
 
6829
    if (!isSessionMachine() && !isSnapshotMachine())
 
6830
    {
 
6831
        // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
 
6832
        if (mData->mFirstSnapshot)
 
6833
        {
 
6834
            // snapshots tree is protected by media write lock; strictly
 
6835
            // this isn't necessary here since we're deleting the entire
 
6836
            // machine, but otherwise we assert in Snapshot::uninit()
 
6837
            AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6838
            mData->mFirstSnapshot->uninit();
 
6839
            mData->mFirstSnapshot.setNull();
 
6840
        }
 
6841
 
 
6842
        mData->mCurrentSnapshot.setNull();
 
6843
    }
 
6844
 
 
6845
    /* free data structures (the essential mData structure is not freed here
 
6846
     * since it may be still in use) */
 
6847
    mMediaData.free();
 
6848
    mStorageControllers.free();
 
6849
    mHWData.free();
 
6850
    mUserData.free();
 
6851
    mSSData.free();
 
6852
}
 
6853
 
 
6854
/**
 
6855
 *  Returns a pointer to the Machine object for this machine that acts like a
 
6856
 *  parent for complex machine data objects such as shared folders, etc.
 
6857
 *
 
6858
 *  For primary Machine objects and for SnapshotMachine objects, returns this
 
6859
 *  object's pointer itself. For SessionMachine objects, returns the peer
 
6860
 *  (primary) machine pointer.
 
6861
 */
 
6862
Machine* Machine::getMachine()
 
6863
{
 
6864
    if (isSessionMachine())
 
6865
        return (Machine*)mPeer;
 
6866
    return this;
 
6867
}
 
6868
 
 
6869
/**
 
6870
 * Makes sure that there are no machine state dependents. If necessary, waits
 
6871
 * for the number of dependents to drop to zero.
 
6872
 *
 
6873
 * Make sure this method is called from under this object's write lock to
 
6874
 * guarantee that no new dependents may be added when this method returns
 
6875
 * control to the caller.
 
6876
 *
 
6877
 * @note Locks this object for writing. The lock will be released while waiting
 
6878
 *       (if necessary).
 
6879
 *
 
6880
 * @warning To be used only in methods that change the machine state!
 
6881
 */
 
6882
void Machine::ensureNoStateDependencies()
 
6883
{
 
6884
    AssertReturnVoid(isWriteLockOnCurrentThread());
 
6885
 
 
6886
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6887
 
 
6888
    /* Wait for all state dependents if necessary */
 
6889
    if (mData->mMachineStateDeps != 0)
 
6890
    {
 
6891
        /* lazy semaphore creation */
 
6892
        if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
 
6893
            RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
 
6894
 
 
6895
        LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
 
6896
                          mData->mMachineStateDeps));
 
6897
 
 
6898
        ++mData->mMachineStateChangePending;
 
6899
 
 
6900
        /* reset the semaphore before waiting, the last dependent will signal
 
6901
         * it */
 
6902
        RTSemEventMultiReset(mData->mMachineStateDepsSem);
 
6903
 
 
6904
        alock.leave();
 
6905
 
 
6906
        RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
 
6907
 
 
6908
        alock.enter();
 
6909
 
 
6910
        -- mData->mMachineStateChangePending;
 
6911
    }
 
6912
}
 
6913
 
 
6914
/**
 
6915
 * Changes the machine state and informs callbacks.
 
6916
 *
 
6917
 * This method is not intended to fail so it either returns S_OK or asserts (and
 
6918
 * returns a failure).
 
6919
 *
 
6920
 * @note Locks this object for writing.
 
6921
 */
 
6922
HRESULT Machine::setMachineState(MachineState_T aMachineState)
 
6923
{
 
6924
    LogFlowThisFuncEnter();
 
6925
    LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
 
6926
 
 
6927
    AutoCaller autoCaller(this);
 
6928
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
6929
 
 
6930
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
6931
 
 
6932
    /* wait for state dependents to drop to zero */
 
6933
    ensureNoStateDependencies();
 
6934
 
 
6935
    if (mData->mMachineState != aMachineState)
 
6936
    {
 
6937
        mData->mMachineState = aMachineState;
 
6938
 
 
6939
        RTTimeNow(&mData->mLastStateChange);
 
6940
 
 
6941
        mParent->onMachineStateChange(mData->mUuid, aMachineState);
 
6942
    }
 
6943
 
 
6944
    LogFlowThisFuncLeave();
 
6945
    return S_OK;
 
6946
}
 
6947
 
 
6948
/**
 
6949
 *  Searches for a shared folder with the given logical name
 
6950
 *  in the collection of shared folders.
 
6951
 *
 
6952
 *  @param aName            logical name of the shared folder
 
6953
 *  @param aSharedFolder    where to return the found object
 
6954
 *  @param aSetError        whether to set the error info if the folder is
 
6955
 *                          not found
 
6956
 *  @return
 
6957
 *      S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
 
6958
 *
 
6959
 *  @note
 
6960
 *      must be called from under the object's lock!
 
6961
 */
 
6962
HRESULT Machine::findSharedFolder(CBSTR aName,
 
6963
                                  ComObjPtr<SharedFolder> &aSharedFolder,
 
6964
                                  bool aSetError /* = false */)
 
6965
{
 
6966
    bool found = false;
 
6967
    for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
 
6968
        !found && it != mHWData->mSharedFolders.end();
 
6969
        ++it)
 
6970
    {
 
6971
        AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
 
6972
        found = (*it)->getName() == aName;
 
6973
        if (found)
 
6974
            aSharedFolder = *it;
 
6975
    }
 
6976
 
 
6977
    HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
 
6978
 
 
6979
    if (aSetError && !found)
 
6980
        setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
 
6981
 
 
6982
    return rc;
 
6983
}
 
6984
 
 
6985
/**
 
6986
 * Initializes all machine instance data from the given settings structures
 
6987
 * from XML. The exception is the machine UUID which needs special handling
 
6988
 * depending on the caller's use case, so the caller needs to set that herself.
 
6989
 *
 
6990
 * This gets called in several contexts during machine initialization:
 
6991
 *
 
6992
 * -- When machine XML exists on disk already and needs to be loaded into memory,
 
6993
 *    for example, from registeredInit() to load all registered machines on
 
6994
 *    VirtualBox startup. In this case, puuidRegistry is NULL because the media
 
6995
 *    attached to the machine should be part of some media registry already.
 
6996
 *
 
6997
 * -- During OVF import, when a machine config has been constructed from an
 
6998
 *    OVF file. In this case, puuidRegistry is set to the machine UUID to
 
6999
 *    ensure that the media listed as attachments in the config (which have
 
7000
 *    been imported from the OVF) receive the correct registry ID.
 
7001
 *
 
7002
 * @param config Machine settings from XML.
 
7003
 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
 
7004
 * @return
 
7005
 */
 
7006
HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
 
7007
                                             const Guid *puuidRegistry)
 
7008
{
 
7009
    // copy name, description, OS type, teleporter, UTC etc.
 
7010
    mUserData->s = config.machineUserData;
 
7011
 
 
7012
    // look up the object by Id to check it is valid
 
7013
    ComPtr<IGuestOSType> guestOSType;
 
7014
    HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
 
7015
                                         guestOSType.asOutParam());
 
7016
    if (FAILED(rc)) return rc;
 
7017
 
 
7018
    // stateFile (optional)
 
7019
    if (config.strStateFile.isEmpty())
 
7020
        mSSData->mStateFilePath.setNull();
 
7021
    else
 
7022
    {
 
7023
        Utf8Str stateFilePathFull(config.strStateFile);
 
7024
        int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
 
7025
        if (RT_FAILURE(vrc))
 
7026
            return setError(E_FAIL,
 
7027
                            tr("Invalid saved state file path '%s' (%Rrc)"),
 
7028
                            config.strStateFile.c_str(),
 
7029
                            vrc);
 
7030
        mSSData->mStateFilePath = stateFilePathFull;
 
7031
    }
 
7032
 
 
7033
    // snapshot folder needs special processing so set it again
 
7034
    rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
 
7035
    if (FAILED(rc)) return rc;
 
7036
 
 
7037
    /* currentStateModified (optional, default is true) */
 
7038
    mData->mCurrentStateModified = config.fCurrentStateModified;
 
7039
 
 
7040
    mData->mLastStateChange = config.timeLastStateChange;
 
7041
 
 
7042
    /*
 
7043
     *  note: all mUserData members must be assigned prior this point because
 
7044
     *  we need to commit changes in order to let mUserData be shared by all
 
7045
     *  snapshot machine instances.
 
7046
     */
 
7047
    mUserData.commitCopy();
 
7048
 
 
7049
    // machine registry, if present (must be loaded before snapshots)
 
7050
    if (config.canHaveOwnMediaRegistry())
 
7051
    {
 
7052
        // determine machine folder
 
7053
        Utf8Str strMachineFolder = getSettingsFileFull();
 
7054
        strMachineFolder.stripFilename();
 
7055
        rc = mParent->initMedia(getId(),         // media registry ID == machine UUID
 
7056
                                config.mediaRegistry,
 
7057
                                strMachineFolder);
 
7058
        if (FAILED(rc)) return rc;
 
7059
    }
 
7060
 
 
7061
    /* Snapshot node (optional) */
 
7062
    size_t cRootSnapshots;
 
7063
    if ((cRootSnapshots = config.llFirstSnapshot.size()))
 
7064
    {
 
7065
        // there must be only one root snapshot
 
7066
        Assert(cRootSnapshots == 1);
 
7067
 
 
7068
        const settings::Snapshot &snap = config.llFirstSnapshot.front();
 
7069
 
 
7070
        rc = loadSnapshot(snap,
 
7071
                          config.uuidCurrentSnapshot,
 
7072
                          NULL);        // no parent == first snapshot
 
7073
        if (FAILED(rc)) return rc;
 
7074
    }
 
7075
 
 
7076
    // hardware data
 
7077
    rc = loadHardware(config.hardwareMachine);
 
7078
    if (FAILED(rc)) return rc;
 
7079
 
 
7080
    // load storage controllers
 
7081
    rc = loadStorageControllers(config.storageMachine,
 
7082
                                puuidRegistry,
 
7083
                                NULL /* puuidSnapshot */);
 
7084
    if (FAILED(rc)) return rc;
 
7085
 
 
7086
    /*
 
7087
     *  NOTE: the assignment below must be the last thing to do,
 
7088
     *  otherwise it will be not possible to change the settings
 
7089
     *  somewhere in the code above because all setters will be
 
7090
     *  blocked by checkStateDependency(MutableStateDep).
 
7091
     */
 
7092
 
 
7093
    /* set the machine state to Aborted or Saved when appropriate */
 
7094
    if (config.fAborted)
 
7095
    {
 
7096
        Assert(!mSSData->mStateFilePath.isEmpty());
 
7097
        mSSData->mStateFilePath.setNull();
 
7098
 
 
7099
        /* no need to use setMachineState() during init() */
 
7100
        mData->mMachineState = MachineState_Aborted;
 
7101
    }
 
7102
    else if (!mSSData->mStateFilePath.isEmpty())
 
7103
    {
 
7104
        /* no need to use setMachineState() during init() */
 
7105
        mData->mMachineState = MachineState_Saved;
 
7106
    }
 
7107
 
 
7108
    // after loading settings, we are no longer different from the XML on disk
 
7109
    mData->flModifications = 0;
 
7110
 
 
7111
    return S_OK;
 
7112
}
 
7113
 
 
7114
/**
 
7115
 *  Recursively loads all snapshots starting from the given.
 
7116
 *
 
7117
 *  @param aNode            <Snapshot> node.
 
7118
 *  @param aCurSnapshotId   Current snapshot ID from the settings file.
 
7119
 *  @param aParentSnapshot  Parent snapshot.
 
7120
 */
 
7121
HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
 
7122
                              const Guid &aCurSnapshotId,
 
7123
                              Snapshot *aParentSnapshot)
 
7124
{
 
7125
    AssertReturn(!isSnapshotMachine(), E_FAIL);
 
7126
    AssertReturn(!isSessionMachine(), E_FAIL);
 
7127
 
 
7128
    HRESULT rc = S_OK;
 
7129
 
 
7130
    Utf8Str strStateFile;
 
7131
    if (!data.strStateFile.isEmpty())
 
7132
    {
 
7133
        /* optional */
 
7134
        strStateFile = data.strStateFile;
 
7135
        int vrc = calculateFullPath(strStateFile, strStateFile);
 
7136
        if (RT_FAILURE(vrc))
 
7137
            return setError(E_FAIL,
 
7138
                            tr("Invalid saved state file path '%s' (%Rrc)"),
 
7139
                            strStateFile.c_str(),
 
7140
                            vrc);
 
7141
    }
 
7142
 
 
7143
    /* create a snapshot machine object */
 
7144
    ComObjPtr<SnapshotMachine> pSnapshotMachine;
 
7145
    pSnapshotMachine.createObject();
 
7146
    rc = pSnapshotMachine->init(this,
 
7147
                                data.hardware,
 
7148
                                data.storage,
 
7149
                                data.uuid.ref(),
 
7150
                                strStateFile);
 
7151
    if (FAILED(rc)) return rc;
 
7152
 
 
7153
    /* create a snapshot object */
 
7154
    ComObjPtr<Snapshot> pSnapshot;
 
7155
    pSnapshot.createObject();
 
7156
    /* initialize the snapshot */
 
7157
    rc = pSnapshot->init(mParent, // VirtualBox object
 
7158
                         data.uuid,
 
7159
                         data.strName,
 
7160
                         data.strDescription,
 
7161
                         data.timestamp,
 
7162
                         pSnapshotMachine,
 
7163
                         aParentSnapshot);
 
7164
    if (FAILED(rc)) return rc;
 
7165
 
 
7166
    /* memorize the first snapshot if necessary */
 
7167
    if (!mData->mFirstSnapshot)
 
7168
        mData->mFirstSnapshot = pSnapshot;
 
7169
 
 
7170
    /* memorize the current snapshot when appropriate */
 
7171
    if (    !mData->mCurrentSnapshot
 
7172
         && pSnapshot->getId() == aCurSnapshotId
 
7173
       )
 
7174
        mData->mCurrentSnapshot = pSnapshot;
 
7175
 
 
7176
    // now create the children
 
7177
    for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
 
7178
         it != data.llChildSnapshots.end();
 
7179
         ++it)
 
7180
    {
 
7181
        const settings::Snapshot &childData = *it;
 
7182
        // recurse
 
7183
        rc = loadSnapshot(childData,
 
7184
                          aCurSnapshotId,
 
7185
                          pSnapshot);       // parent = the one we created above
 
7186
        if (FAILED(rc)) return rc;
 
7187
    }
 
7188
 
 
7189
    return rc;
 
7190
}
 
7191
 
 
7192
/**
 
7193
 *  @param aNode    <Hardware> node.
 
7194
 */
 
7195
HRESULT Machine::loadHardware(const settings::Hardware &data)
 
7196
{
 
7197
    AssertReturn(!isSessionMachine(), E_FAIL);
 
7198
 
 
7199
    HRESULT rc = S_OK;
 
7200
 
 
7201
    try
 
7202
    {
 
7203
        /* The hardware version attribute (optional). */
 
7204
        mHWData->mHWVersion = data.strVersion;
 
7205
        mHWData->mHardwareUUID = data.uuid;
 
7206
 
 
7207
        mHWData->mHWVirtExEnabled             = data.fHardwareVirt;
 
7208
        mHWData->mHWVirtExExclusive           = data.fHardwareVirtExclusive;
 
7209
        mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
 
7210
        mHWData->mHWVirtExLargePagesEnabled   = data.fLargePages;
 
7211
        mHWData->mHWVirtExVPIDEnabled         = data.fVPID;
 
7212
        mHWData->mHWVirtExForceEnabled        = data.fHardwareVirtForce;
 
7213
        mHWData->mPAEEnabled                  = data.fPAE;
 
7214
        mHWData->mSyntheticCpu                = data.fSyntheticCpu;
 
7215
 
 
7216
        mHWData->mCPUCount                    = data.cCPUs;
 
7217
        mHWData->mCPUHotPlugEnabled           = data.fCpuHotPlug;
 
7218
        mHWData->mCpuExecutionCap             = data.ulCpuExecutionCap;
 
7219
 
 
7220
        // cpu
 
7221
        if (mHWData->mCPUHotPlugEnabled)
 
7222
        {
 
7223
            for (settings::CpuList::const_iterator it = data.llCpus.begin();
 
7224
                it != data.llCpus.end();
 
7225
                ++it)
 
7226
            {
 
7227
                const settings::Cpu &cpu = *it;
 
7228
 
 
7229
                mHWData->mCPUAttached[cpu.ulId] = true;
 
7230
            }
 
7231
        }
 
7232
 
 
7233
        // cpuid leafs
 
7234
        for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
 
7235
            it != data.llCpuIdLeafs.end();
 
7236
            ++it)
 
7237
        {
 
7238
            const settings::CpuIdLeaf &leaf = *it;
 
7239
 
 
7240
            switch (leaf.ulId)
 
7241
            {
 
7242
            case 0x0:
 
7243
            case 0x1:
 
7244
            case 0x2:
 
7245
            case 0x3:
 
7246
            case 0x4:
 
7247
            case 0x5:
 
7248
            case 0x6:
 
7249
            case 0x7:
 
7250
            case 0x8:
 
7251
            case 0x9:
 
7252
            case 0xA:
 
7253
                mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
 
7254
                break;
 
7255
 
 
7256
            case 0x80000000:
 
7257
            case 0x80000001:
 
7258
            case 0x80000002:
 
7259
            case 0x80000003:
 
7260
            case 0x80000004:
 
7261
            case 0x80000005:
 
7262
            case 0x80000006:
 
7263
            case 0x80000007:
 
7264
            case 0x80000008:
 
7265
            case 0x80000009:
 
7266
            case 0x8000000A:
 
7267
                mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
 
7268
                break;
 
7269
 
 
7270
            default:
 
7271
                /* just ignore */
 
7272
                break;
 
7273
            }
 
7274
        }
 
7275
 
 
7276
        mHWData->mMemorySize = data.ulMemorySizeMB;
 
7277
        mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
 
7278
 
 
7279
        // boot order
 
7280
        for (size_t i = 0;
 
7281
             i < RT_ELEMENTS(mHWData->mBootOrder);
 
7282
             i++)
 
7283
        {
 
7284
            settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
 
7285
            if (it == data.mapBootOrder.end())
 
7286
                mHWData->mBootOrder[i] = DeviceType_Null;
 
7287
            else
 
7288
                mHWData->mBootOrder[i] = it->second;
 
7289
        }
 
7290
 
 
7291
        mHWData->mVRAMSize      = data.ulVRAMSizeMB;
 
7292
        mHWData->mMonitorCount  = data.cMonitors;
 
7293
        mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
 
7294
        mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
 
7295
        mHWData->mFirmwareType = data.firmwareType;
 
7296
        mHWData->mPointingHidType = data.pointingHidType;
 
7297
        mHWData->mKeyboardHidType = data.keyboardHidType;
 
7298
        mHWData->mChipsetType = data.chipsetType;
 
7299
        mHWData->mHpetEnabled = data.fHpetEnabled;
 
7300
 
 
7301
        /* VRDEServer */
 
7302
        rc = mVRDEServer->loadSettings(data.vrdeSettings);
 
7303
        if (FAILED(rc)) return rc;
 
7304
 
 
7305
        /* BIOS */
 
7306
        rc = mBIOSSettings->loadSettings(data.biosSettings);
 
7307
        if (FAILED(rc)) return rc;
 
7308
 
 
7309
        /* USB Controller */
 
7310
        rc = mUSBController->loadSettings(data.usbController);
 
7311
        if (FAILED(rc)) return rc;
 
7312
 
 
7313
        // network adapters
 
7314
        for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
 
7315
            it != data.llNetworkAdapters.end();
 
7316
            ++it)
 
7317
        {
 
7318
            const settings::NetworkAdapter &nic = *it;
 
7319
 
 
7320
            /* slot unicity is guaranteed by XML Schema */
 
7321
            AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
 
7322
            rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
 
7323
            if (FAILED(rc)) return rc;
 
7324
        }
 
7325
 
 
7326
        // serial ports
 
7327
        for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
 
7328
            it != data.llSerialPorts.end();
 
7329
            ++it)
 
7330
        {
 
7331
            const settings::SerialPort &s = *it;
 
7332
 
 
7333
            AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
 
7334
            rc = mSerialPorts[s.ulSlot]->loadSettings(s);
 
7335
            if (FAILED(rc)) return rc;
 
7336
        }
 
7337
 
 
7338
        // parallel ports (optional)
 
7339
        for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
 
7340
            it != data.llParallelPorts.end();
 
7341
            ++it)
 
7342
        {
 
7343
            const settings::ParallelPort &p = *it;
 
7344
 
 
7345
            AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
 
7346
            rc = mParallelPorts[p.ulSlot]->loadSettings(p);
 
7347
            if (FAILED(rc)) return rc;
 
7348
        }
 
7349
 
 
7350
        /* AudioAdapter */
 
7351
        rc = mAudioAdapter->loadSettings(data.audioAdapter);
 
7352
        if (FAILED(rc)) return rc;
 
7353
 
 
7354
        for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
 
7355
             it != data.llSharedFolders.end();
 
7356
             ++it)
 
7357
        {
 
7358
            const settings::SharedFolder &sf = *it;
 
7359
            rc = CreateSharedFolder(Bstr(sf.strName).raw(),
 
7360
                                    Bstr(sf.strHostPath).raw(),
 
7361
                                    sf.fWritable, sf.fAutoMount);
 
7362
            if (FAILED(rc)) return rc;
 
7363
        }
 
7364
 
 
7365
        // Clipboard
 
7366
        mHWData->mClipboardMode = data.clipboardMode;
 
7367
 
 
7368
        // guest settings
 
7369
        mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
 
7370
 
 
7371
        // IO settings
 
7372
        mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
 
7373
        mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
 
7374
 
 
7375
        // Bandwidth control
 
7376
        rc = mBandwidthControl->loadSettings(data.ioSettings);
 
7377
        if (FAILED(rc)) return rc;
 
7378
 
 
7379
#ifdef VBOX_WITH_GUEST_PROPS
 
7380
        /* Guest properties (optional) */
 
7381
        for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
 
7382
            it != data.llGuestProperties.end();
 
7383
            ++it)
 
7384
        {
 
7385
            const settings::GuestProperty &prop = *it;
 
7386
            uint32_t fFlags = guestProp::NILFLAG;
 
7387
            guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
 
7388
            HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
 
7389
            mHWData->mGuestProperties.push_back(property);
 
7390
        }
 
7391
 
 
7392
        mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
 
7393
#endif /* VBOX_WITH_GUEST_PROPS defined */
 
7394
    }
 
7395
    catch(std::bad_alloc &)
 
7396
    {
 
7397
        return E_OUTOFMEMORY;
 
7398
    }
 
7399
 
 
7400
    AssertComRC(rc);
 
7401
    return rc;
 
7402
}
 
7403
 
 
7404
/**
 
7405
 *  Called from loadMachineDataFromSettings() for the storage controller data, including media.
 
7406
 *
 
7407
 * @param data
 
7408
 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
 
7409
 * @param puuidSnapshot
 
7410
 * @return
 
7411
 */
 
7412
HRESULT Machine::loadStorageControllers(const settings::Storage &data,
 
7413
                                        const Guid *puuidRegistry,
 
7414
                                        const Guid *puuidSnapshot)
 
7415
{
 
7416
    AssertReturn(!isSessionMachine(), E_FAIL);
 
7417
 
 
7418
    HRESULT rc = S_OK;
 
7419
 
 
7420
    for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
 
7421
         it != data.llStorageControllers.end();
 
7422
         ++it)
 
7423
    {
 
7424
        const settings::StorageController &ctlData = *it;
 
7425
 
 
7426
        ComObjPtr<StorageController> pCtl;
 
7427
        /* Try to find one with the name first. */
 
7428
        rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
 
7429
        if (SUCCEEDED(rc))
 
7430
            return setError(VBOX_E_OBJECT_IN_USE,
 
7431
                            tr("Storage controller named '%s' already exists"),
 
7432
                            ctlData.strName.c_str());
 
7433
 
 
7434
        pCtl.createObject();
 
7435
        rc = pCtl->init(this,
 
7436
                        ctlData.strName,
 
7437
                        ctlData.storageBus,
 
7438
                        ctlData.ulInstance,
 
7439
                        ctlData.fBootable);
 
7440
        if (FAILED(rc)) return rc;
 
7441
 
 
7442
        mStorageControllers->push_back(pCtl);
 
7443
 
 
7444
        rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
 
7445
        if (FAILED(rc)) return rc;
 
7446
 
 
7447
        rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
 
7448
        if (FAILED(rc)) return rc;
 
7449
 
 
7450
        rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
 
7451
        if (FAILED(rc)) return rc;
 
7452
 
 
7453
        /* Set IDE emulation settings (only for AHCI controller). */
 
7454
        if (ctlData.controllerType == StorageControllerType_IntelAhci)
 
7455
        {
 
7456
            if (    (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
 
7457
                 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
 
7458
                 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
 
7459
                 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
 
7460
               )
 
7461
                return rc;
 
7462
        }
 
7463
 
 
7464
        /* Load the attached devices now. */
 
7465
        rc = loadStorageDevices(pCtl,
 
7466
                                ctlData,
 
7467
                                puuidRegistry,
 
7468
                                puuidSnapshot);
 
7469
        if (FAILED(rc)) return rc;
 
7470
    }
 
7471
 
 
7472
    return S_OK;
 
7473
}
 
7474
 
 
7475
/**
 
7476
 * Called from loadStorageControllers for a controller's devices.
 
7477
 *
 
7478
 * @param aStorageController
 
7479
 * @param data
 
7480
 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
 
7481
 * @param aSnapshotId  pointer to the snapshot ID if this is a snapshot machine
 
7482
 * @return
 
7483
 */
 
7484
HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
 
7485
                                    const settings::StorageController &data,
 
7486
                                    const Guid *puuidRegistry,
 
7487
                                    const Guid *puuidSnapshot)
 
7488
{
 
7489
    HRESULT rc = S_OK;
 
7490
 
 
7491
    /* paranoia: detect duplicate attachments */
 
7492
    for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
 
7493
         it != data.llAttachedDevices.end();
 
7494
         ++it)
 
7495
    {
 
7496
        const settings::AttachedDevice &ad = *it;
 
7497
 
 
7498
        for (settings::AttachedDevicesList::const_iterator it2 = it;
 
7499
             it2 != data.llAttachedDevices.end();
 
7500
             ++it2)
 
7501
        {
 
7502
            if (it == it2)
 
7503
                continue;
 
7504
 
 
7505
            const settings::AttachedDevice &ad2 = *it2;
 
7506
 
 
7507
            if (   ad.lPort == ad2.lPort
 
7508
                && ad.lDevice == ad2.lDevice)
 
7509
            {
 
7510
                return setError(E_FAIL,
 
7511
                                tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
 
7512
                                aStorageController->getName().c_str(),
 
7513
                                ad.lPort,
 
7514
                                ad.lDevice,
 
7515
                                mUserData->s.strName.c_str());
 
7516
            }
 
7517
        }
 
7518
    }
 
7519
 
 
7520
    for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
 
7521
         it != data.llAttachedDevices.end();
 
7522
         ++it)
 
7523
    {
 
7524
        const settings::AttachedDevice &dev = *it;
 
7525
        ComObjPtr<Medium> medium;
 
7526
 
 
7527
        switch (dev.deviceType)
 
7528
        {
 
7529
            case DeviceType_Floppy:
 
7530
            case DeviceType_DVD:
 
7531
                if (dev.strHostDriveSrc.isNotEmpty())
 
7532
                    rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
 
7533
                else
 
7534
                    rc = mParent->findRemoveableMedium(dev.deviceType,
 
7535
                                                       dev.uuid,
 
7536
                                                       false /* fRefresh */,
 
7537
                                                       false /* aSetError */,
 
7538
                                                       medium);
 
7539
                if (rc == VBOX_E_OBJECT_NOT_FOUND)
 
7540
                    // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
 
7541
                    rc = S_OK;
 
7542
            break;
 
7543
 
 
7544
            case DeviceType_HardDisk:
 
7545
            {
 
7546
                /* find a hard disk by UUID */
 
7547
                rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
 
7548
                if (FAILED(rc))
 
7549
                {
 
7550
                    if (isSnapshotMachine())
 
7551
                    {
 
7552
                        // wrap another error message around the "cannot find hard disk" set by findHardDisk
 
7553
                        // so the user knows that the bad disk is in a snapshot somewhere
 
7554
                        com::ErrorInfo info;
 
7555
                        return setError(E_FAIL,
 
7556
                                        tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
 
7557
                                        puuidSnapshot->raw(),
 
7558
                                        info.getText().raw());
 
7559
                    }
 
7560
                    else
 
7561
                        return rc;
 
7562
                }
 
7563
 
 
7564
                AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
 
7565
 
 
7566
                if (medium->getType() == MediumType_Immutable)
 
7567
                {
 
7568
                    if (isSnapshotMachine())
 
7569
                        return setError(E_FAIL,
 
7570
                                        tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
 
7571
                                           "of the virtual machine '%s' ('%s')"),
 
7572
                                        medium->getLocationFull().c_str(),
 
7573
                                        dev.uuid.raw(),
 
7574
                                        puuidSnapshot->raw(),
 
7575
                                        mUserData->s.strName.c_str(),
 
7576
                                        mData->m_strConfigFileFull.c_str());
 
7577
 
 
7578
                    return setError(E_FAIL,
 
7579
                                    tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
 
7580
                                    medium->getLocationFull().c_str(),
 
7581
                                    dev.uuid.raw(),
 
7582
                                    mUserData->s.strName.c_str(),
 
7583
                                    mData->m_strConfigFileFull.c_str());
 
7584
                }
 
7585
 
 
7586
                if (medium->getType() == MediumType_MultiAttach)
 
7587
                {
 
7588
                    if (isSnapshotMachine())
 
7589
                        return setError(E_FAIL,
 
7590
                                        tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
 
7591
                                           "of the virtual machine '%s' ('%s')"),
 
7592
                                        medium->getLocationFull().c_str(),
 
7593
                                        dev.uuid.raw(),
 
7594
                                        puuidSnapshot->raw(),
 
7595
                                        mUserData->s.strName.c_str(),
 
7596
                                        mData->m_strConfigFileFull.c_str());
 
7597
 
 
7598
                    return setError(E_FAIL,
 
7599
                                    tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
 
7600
                                    medium->getLocationFull().c_str(),
 
7601
                                    dev.uuid.raw(),
 
7602
                                    mUserData->s.strName.c_str(),
 
7603
                                    mData->m_strConfigFileFull.c_str());
 
7604
                }
 
7605
 
 
7606
                if (    !isSnapshotMachine()
 
7607
                     && medium->getChildren().size() != 0
 
7608
                   )
 
7609
                    return setError(E_FAIL,
 
7610
                                    tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
 
7611
                                       "because it has %d differencing child hard disks"),
 
7612
                                    medium->getLocationFull().c_str(),
 
7613
                                    dev.uuid.raw(),
 
7614
                                    mUserData->s.strName.c_str(),
 
7615
                                    mData->m_strConfigFileFull.c_str(),
 
7616
                                    medium->getChildren().size());
 
7617
 
 
7618
                if (findAttachment(mMediaData->mAttachments,
 
7619
                                   medium))
 
7620
                    return setError(E_FAIL,
 
7621
                                    tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
 
7622
                                    medium->getLocationFull().c_str(),
 
7623
                                    dev.uuid.raw(),
 
7624
                                    mUserData->s.strName.c_str(),
 
7625
                                    mData->m_strConfigFileFull.c_str());
 
7626
 
 
7627
                break;
 
7628
            }
 
7629
 
 
7630
            default:
 
7631
                return setError(E_FAIL,
 
7632
                                tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
 
7633
                                medium->getLocationFull().c_str(),
 
7634
                                mUserData->s.strName.c_str(),
 
7635
                                mData->m_strConfigFileFull.c_str());
 
7636
        }
 
7637
 
 
7638
        if (FAILED(rc))
 
7639
            break;
 
7640
 
 
7641
        /* Bandwidth groups are loaded at this point. */
 
7642
        ComObjPtr<BandwidthGroup> pBwGroup;
 
7643
 
 
7644
        if (!dev.strBwGroup.isEmpty())
 
7645
        {
 
7646
            rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
 
7647
            if (FAILED(rc))
 
7648
                return setError(E_FAIL,
 
7649
                                tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
 
7650
                                medium->getLocationFull().c_str(),
 
7651
                                dev.strBwGroup.c_str(),
 
7652
                                mUserData->s.strName.c_str(),
 
7653
                                mData->m_strConfigFileFull.c_str());
 
7654
        }
 
7655
 
 
7656
        const Bstr controllerName = aStorageController->getName();
 
7657
        ComObjPtr<MediumAttachment> pAttachment;
 
7658
        pAttachment.createObject();
 
7659
        rc = pAttachment->init(this,
 
7660
                               medium,
 
7661
                               controllerName,
 
7662
                               dev.lPort,
 
7663
                               dev.lDevice,
 
7664
                               dev.deviceType,
 
7665
                               dev.fPassThrough,
 
7666
                               pBwGroup);
 
7667
        if (FAILED(rc)) break;
 
7668
 
 
7669
        /* associate the medium with this machine and snapshot */
 
7670
        if (!medium.isNull())
 
7671
        {
 
7672
            if (isSnapshotMachine())
 
7673
                rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
 
7674
            else
 
7675
                rc = medium->addBackReference(mData->mUuid);
 
7676
 
 
7677
            if (puuidRegistry)
 
7678
                // caller wants registry ID to be set on all attached media (OVF import case)
 
7679
                medium->addRegistry(*puuidRegistry);
 
7680
        }
 
7681
 
 
7682
        if (FAILED(rc))
 
7683
            break;
 
7684
 
 
7685
        /* back up mMediaData to let registeredInit() properly rollback on failure
 
7686
         * (= limited accessibility) */
 
7687
        setModified(IsModified_Storage);
 
7688
        mMediaData.backup();
 
7689
        mMediaData->mAttachments.push_back(pAttachment);
 
7690
    }
 
7691
 
 
7692
    return rc;
 
7693
}
 
7694
 
 
7695
/**
 
7696
 *  Returns the snapshot with the given UUID or fails of no such snapshot exists.
 
7697
 *
 
7698
 *  @param aId          snapshot UUID to find (empty UUID refers the first snapshot)
 
7699
 *  @param aSnapshot    where to return the found snapshot
 
7700
 *  @param aSetError    true to set extended error info on failure
 
7701
 */
 
7702
HRESULT Machine::findSnapshotById(const Guid &aId,
 
7703
                                  ComObjPtr<Snapshot> &aSnapshot,
 
7704
                                  bool aSetError /* = false */)
 
7705
{
 
7706
    AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
 
7707
 
 
7708
    if (!mData->mFirstSnapshot)
 
7709
    {
 
7710
        if (aSetError)
 
7711
            return setError(E_FAIL, tr("This machine does not have any snapshots"));
 
7712
        return E_FAIL;
 
7713
    }
 
7714
 
 
7715
    if (aId.isEmpty())
 
7716
        aSnapshot = mData->mFirstSnapshot;
 
7717
    else
 
7718
        aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
 
7719
 
 
7720
    if (!aSnapshot)
 
7721
    {
 
7722
        if (aSetError)
 
7723
            return setError(E_FAIL,
 
7724
                            tr("Could not find a snapshot with UUID {%s}"),
 
7725
                            aId.toString().c_str());
 
7726
        return E_FAIL;
 
7727
    }
 
7728
 
 
7729
    return S_OK;
 
7730
}
 
7731
 
 
7732
/**
 
7733
 *  Returns the snapshot with the given name or fails of no such snapshot.
 
7734
 *
 
7735
 *  @param aName        snapshot name to find
 
7736
 *  @param aSnapshot    where to return the found snapshot
 
7737
 *  @param aSetError    true to set extended error info on failure
 
7738
 */
 
7739
HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
 
7740
                                    ComObjPtr<Snapshot> &aSnapshot,
 
7741
                                    bool aSetError /* = false */)
 
7742
{
 
7743
    AssertReturn(!strName.isEmpty(), E_INVALIDARG);
 
7744
 
 
7745
    AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
 
7746
 
 
7747
    if (!mData->mFirstSnapshot)
 
7748
    {
 
7749
        if (aSetError)
 
7750
            return setError(VBOX_E_OBJECT_NOT_FOUND,
 
7751
                            tr("This machine does not have any snapshots"));
 
7752
        return VBOX_E_OBJECT_NOT_FOUND;
 
7753
    }
 
7754
 
 
7755
    aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
 
7756
 
 
7757
    if (!aSnapshot)
 
7758
    {
 
7759
        if (aSetError)
 
7760
            return setError(VBOX_E_OBJECT_NOT_FOUND,
 
7761
                            tr("Could not find a snapshot named '%s'"), strName.c_str());
 
7762
        return VBOX_E_OBJECT_NOT_FOUND;
 
7763
    }
 
7764
 
 
7765
    return S_OK;
 
7766
}
 
7767
 
 
7768
/**
 
7769
 * Returns a storage controller object with the given name.
 
7770
 *
 
7771
 *  @param aName                 storage controller name to find
 
7772
 *  @param aStorageController    where to return the found storage controller
 
7773
 *  @param aSetError             true to set extended error info on failure
 
7774
 */
 
7775
HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
 
7776
                                            ComObjPtr<StorageController> &aStorageController,
 
7777
                                            bool aSetError /* = false */)
 
7778
{
 
7779
    AssertReturn(!aName.isEmpty(), E_INVALIDARG);
 
7780
 
 
7781
    for (StorageControllerList::const_iterator it = mStorageControllers->begin();
 
7782
         it != mStorageControllers->end();
 
7783
         ++it)
 
7784
    {
 
7785
        if ((*it)->getName() == aName)
 
7786
        {
 
7787
            aStorageController = (*it);
 
7788
            return S_OK;
 
7789
        }
 
7790
    }
 
7791
 
 
7792
    if (aSetError)
 
7793
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
7794
                        tr("Could not find a storage controller named '%s'"),
 
7795
                        aName.c_str());
 
7796
    return VBOX_E_OBJECT_NOT_FOUND;
 
7797
}
 
7798
 
 
7799
HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
 
7800
                                                  MediaData::AttachmentList &atts)
 
7801
{
 
7802
    AutoCaller autoCaller(this);
 
7803
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
7804
 
 
7805
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
7806
 
 
7807
    for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
 
7808
         it != mMediaData->mAttachments.end();
 
7809
         ++it)
 
7810
    {
 
7811
        const ComObjPtr<MediumAttachment> &pAtt = *it;
 
7812
 
 
7813
        // should never happen, but deal with NULL pointers in the list.
 
7814
        AssertStmt(!pAtt.isNull(), continue);
 
7815
 
 
7816
        // getControllerName() needs caller+read lock
 
7817
        AutoCaller autoAttCaller(pAtt);
 
7818
        if (FAILED(autoAttCaller.rc()))
 
7819
        {
 
7820
            atts.clear();
 
7821
            return autoAttCaller.rc();
 
7822
        }
 
7823
        AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
 
7824
 
 
7825
        if (pAtt->getControllerName() == aName)
 
7826
            atts.push_back(pAtt);
 
7827
    }
 
7828
 
 
7829
    return S_OK;
 
7830
}
 
7831
 
 
7832
/**
 
7833
 *  Helper for #saveSettings. Cares about renaming the settings directory and
 
7834
 *  file if the machine name was changed and about creating a new settings file
 
7835
 *  if this is a new machine.
 
7836
 *
 
7837
 *  @note Must be never called directly but only from #saveSettings().
 
7838
 */
 
7839
HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
 
7840
{
 
7841
    AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
 
7842
 
 
7843
    HRESULT rc = S_OK;
 
7844
 
 
7845
    bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
 
7846
 
 
7847
    /* attempt to rename the settings file if machine name is changed */
 
7848
    if (    mUserData->s.fNameSync
 
7849
         && mUserData.isBackedUp()
 
7850
         && mUserData.backedUpData()->s.strName != mUserData->s.strName
 
7851
       )
 
7852
    {
 
7853
        bool dirRenamed = false;
 
7854
        bool fileRenamed = false;
 
7855
 
 
7856
        Utf8Str configFile, newConfigFile;
 
7857
        Utf8Str configDir, newConfigDir;
 
7858
 
 
7859
        do
 
7860
        {
 
7861
            int vrc = VINF_SUCCESS;
 
7862
 
 
7863
            Utf8Str name = mUserData.backedUpData()->s.strName;
 
7864
            Utf8Str newName = mUserData->s.strName;
 
7865
 
 
7866
            configFile = mData->m_strConfigFileFull;
 
7867
 
 
7868
            /* first, rename the directory if it matches the machine name */
 
7869
            configDir = configFile;
 
7870
            configDir.stripFilename();
 
7871
            newConfigDir = configDir;
 
7872
            if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
 
7873
            {
 
7874
                newConfigDir.stripFilename();
 
7875
                newConfigDir.append(RTPATH_DELIMITER);
 
7876
                newConfigDir.append(newName);
 
7877
                /* new dir and old dir cannot be equal here because of 'if'
 
7878
                 * above and because name != newName */
 
7879
                Assert(configDir != newConfigDir);
 
7880
                if (!fSettingsFileIsNew)
 
7881
                {
 
7882
                    /* perform real rename only if the machine is not new */
 
7883
                    vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
 
7884
                    if (RT_FAILURE(vrc))
 
7885
                    {
 
7886
                        rc = setError(E_FAIL,
 
7887
                                      tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
 
7888
                                      configDir.c_str(),
 
7889
                                      newConfigDir.c_str(),
 
7890
                                      vrc);
 
7891
                        break;
 
7892
                    }
 
7893
                    dirRenamed = true;
 
7894
                }
 
7895
            }
 
7896
 
 
7897
            newConfigFile = Utf8StrFmt("%s%c%s.vbox",
 
7898
                newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
 
7899
 
 
7900
            /* then try to rename the settings file itself */
 
7901
            if (newConfigFile != configFile)
 
7902
            {
 
7903
                /* get the path to old settings file in renamed directory */
 
7904
                configFile = Utf8StrFmt("%s%c%s",
 
7905
                                        newConfigDir.c_str(),
 
7906
                                        RTPATH_DELIMITER,
 
7907
                                        RTPathFilename(configFile.c_str()));
 
7908
                if (!fSettingsFileIsNew)
 
7909
                {
 
7910
                    /* perform real rename only if the machine is not new */
 
7911
                    vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
 
7912
                    if (RT_FAILURE(vrc))
 
7913
                    {
 
7914
                        rc = setError(E_FAIL,
 
7915
                                      tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
 
7916
                                      configFile.c_str(),
 
7917
                                      newConfigFile.c_str(),
 
7918
                                      vrc);
 
7919
                        break;
 
7920
                    }
 
7921
                    fileRenamed = true;
 
7922
                }
 
7923
            }
 
7924
 
 
7925
            // update m_strConfigFileFull amd mConfigFile
 
7926
            mData->m_strConfigFileFull = newConfigFile;
 
7927
            // compute the relative path too
 
7928
            mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
 
7929
 
 
7930
            // store the old and new so that VirtualBox::saveSettings() can update
 
7931
            // the media registry
 
7932
            if (    mData->mRegistered
 
7933
                 && configDir != newConfigDir)
 
7934
            {
 
7935
                mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
 
7936
 
 
7937
                if (pfNeedsGlobalSaveSettings)
 
7938
                    *pfNeedsGlobalSaveSettings = true;
 
7939
            }
 
7940
 
 
7941
            /* update the saved state file path */
 
7942
            Utf8Str path = mSSData->mStateFilePath;
 
7943
            if (RTPathStartsWith(path.c_str(), configDir.c_str()))
 
7944
                mSSData->mStateFilePath = Utf8StrFmt("%s%s",
 
7945
                                                     newConfigDir.c_str(),
 
7946
                                                     path.c_str() + configDir.length());
 
7947
 
 
7948
            /* Update saved state file paths of all online snapshots.
 
7949
             * Note that saveSettings() will recognize name change
 
7950
             * and will save all snapshots in this case. */
 
7951
            if (mData->mFirstSnapshot)
 
7952
                mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
 
7953
                                                             newConfigDir.c_str());
 
7954
        }
 
7955
        while (0);
 
7956
 
 
7957
        if (FAILED(rc))
 
7958
        {
 
7959
            /* silently try to rename everything back */
 
7960
            if (fileRenamed)
 
7961
                RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
 
7962
            if (dirRenamed)
 
7963
                RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
 
7964
        }
 
7965
 
 
7966
        if (FAILED(rc)) return rc;
 
7967
    }
 
7968
 
 
7969
    if (fSettingsFileIsNew)
 
7970
    {
 
7971
        /* create a virgin config file */
 
7972
        int vrc = VINF_SUCCESS;
 
7973
 
 
7974
        /* ensure the settings directory exists */
 
7975
        Utf8Str path(mData->m_strConfigFileFull);
 
7976
        path.stripFilename();
 
7977
        if (!RTDirExists(path.c_str()))
 
7978
        {
 
7979
            vrc = RTDirCreateFullPath(path.c_str(), 0777);
 
7980
            if (RT_FAILURE(vrc))
 
7981
            {
 
7982
                return setError(E_FAIL,
 
7983
                                tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
 
7984
                                path.c_str(),
 
7985
                                vrc);
 
7986
            }
 
7987
        }
 
7988
 
 
7989
        /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
 
7990
        path = Utf8Str(mData->m_strConfigFileFull);
 
7991
        RTFILE f = NIL_RTFILE;
 
7992
        vrc = RTFileOpen(&f, path.c_str(),
 
7993
                         RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
 
7994
        if (RT_FAILURE(vrc))
 
7995
            return setError(E_FAIL,
 
7996
                            tr("Could not create the settings file '%s' (%Rrc)"),
 
7997
                            path.c_str(),
 
7998
                            vrc);
 
7999
        RTFileClose(f);
 
8000
    }
 
8001
 
 
8002
    return rc;
 
8003
}
 
8004
 
 
8005
/**
 
8006
 * Saves and commits machine data, user data and hardware data.
 
8007
 *
 
8008
 * Note that on failure, the data remains uncommitted.
 
8009
 *
 
8010
 * @a aFlags may combine the following flags:
 
8011
 *
 
8012
 *  - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
 
8013
 *    Used when saving settings after an operation that makes them 100%
 
8014
 *    correspond to the settings from the current snapshot.
 
8015
 *  - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
 
8016
 *    #isReallyModified() returns false. This is necessary for cases when we
 
8017
 *    change machine data directly, not through the backup()/commit() mechanism.
 
8018
 *  - SaveS_Force: settings will be saved without doing a deep compare of the
 
8019
 *    settings structures. This is used when this is called because snapshots
 
8020
 *    have changed to avoid the overhead of the deep compare.
 
8021
 *
 
8022
 * @note Must be called from under this object's write lock. Locks children for
 
8023
 * writing.
 
8024
 *
 
8025
 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
 
8026
 *          initialized to false and that will be set to true by this function if
 
8027
 *          the caller must invoke VirtualBox::saveSettings() because the global
 
8028
 *          settings have changed. This will happen if a machine rename has been
 
8029
 *          saved and the global machine and media registries will therefore need
 
8030
 *          updating.
 
8031
 */
 
8032
HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
 
8033
                              int aFlags /*= 0*/)
 
8034
{
 
8035
    LogFlowThisFuncEnter();
 
8036
 
 
8037
    AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
 
8038
 
 
8039
    /* make sure child objects are unable to modify the settings while we are
 
8040
     * saving them */
 
8041
    ensureNoStateDependencies();
 
8042
 
 
8043
    AssertReturn(!isSnapshotMachine(),
 
8044
                 E_FAIL);
 
8045
 
 
8046
    HRESULT rc = S_OK;
 
8047
    bool fNeedsWrite = false;
 
8048
 
 
8049
    /* First, prepare to save settings. It will care about renaming the
 
8050
     * settings directory and file if the machine name was changed and about
 
8051
     * creating a new settings file if this is a new machine. */
 
8052
    rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
 
8053
    if (FAILED(rc)) return rc;
 
8054
 
 
8055
    // keep a pointer to the current settings structures
 
8056
    settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
 
8057
    settings::MachineConfigFile *pNewConfig = NULL;
 
8058
 
 
8059
    try
 
8060
    {
 
8061
        // make a fresh one to have everyone write stuff into
 
8062
        pNewConfig = new settings::MachineConfigFile(NULL);
 
8063
        pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
 
8064
 
 
8065
        // now go and copy all the settings data from COM to the settings structures
 
8066
        // (this calles saveSettings() on all the COM objects in the machine)
 
8067
        copyMachineDataToSettings(*pNewConfig);
 
8068
 
 
8069
        if (aFlags & SaveS_ResetCurStateModified)
 
8070
        {
 
8071
            // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
 
8072
            mData->mCurrentStateModified = FALSE;
 
8073
            fNeedsWrite = true;     // always, no need to compare
 
8074
        }
 
8075
        else if (aFlags & SaveS_Force)
 
8076
        {
 
8077
            fNeedsWrite = true;     // always, no need to compare
 
8078
        }
 
8079
        else
 
8080
        {
 
8081
            if (!mData->mCurrentStateModified)
 
8082
            {
 
8083
                // do a deep compare of the settings that we just saved with the settings
 
8084
                // previously stored in the config file; this invokes MachineConfigFile::operator==
 
8085
                // which does a deep compare of all the settings, which is expensive but less expensive
 
8086
                // than writing out XML in vain
 
8087
                bool fAnySettingsChanged = (*pNewConfig == *pOldConfig);
 
8088
 
 
8089
                // could still be modified if any settings changed
 
8090
                mData->mCurrentStateModified = fAnySettingsChanged;
 
8091
 
 
8092
                fNeedsWrite = fAnySettingsChanged;
 
8093
            }
 
8094
            else
 
8095
                fNeedsWrite = true;
 
8096
        }
 
8097
 
 
8098
        pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
 
8099
 
 
8100
        if (fNeedsWrite)
 
8101
            // now spit it all out!
 
8102
            pNewConfig->write(mData->m_strConfigFileFull);
 
8103
 
 
8104
        mData->pMachineConfigFile = pNewConfig;
 
8105
        delete pOldConfig;
 
8106
        commit();
 
8107
 
 
8108
        // after saving settings, we are no longer different from the XML on disk
 
8109
        mData->flModifications = 0;
 
8110
    }
 
8111
    catch (HRESULT err)
 
8112
    {
 
8113
        // we assume that error info is set by the thrower
 
8114
        rc = err;
 
8115
 
 
8116
        // restore old config
 
8117
        delete pNewConfig;
 
8118
        mData->pMachineConfigFile = pOldConfig;
 
8119
    }
 
8120
    catch (...)
 
8121
    {
 
8122
        rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
 
8123
    }
 
8124
 
 
8125
    if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
 
8126
    {
 
8127
        /* Fire the data change event, even on failure (since we've already
 
8128
         * committed all data). This is done only for SessionMachines because
 
8129
         * mutable Machine instances are always not registered (i.e. private
 
8130
         * to the client process that creates them) and thus don't need to
 
8131
         * inform callbacks. */
 
8132
        if (isSessionMachine())
 
8133
            mParent->onMachineDataChange(mData->mUuid);
 
8134
    }
 
8135
 
 
8136
    LogFlowThisFunc(("rc=%08X\n", rc));
 
8137
    LogFlowThisFuncLeave();
 
8138
    return rc;
 
8139
}
 
8140
 
 
8141
/**
 
8142
 * Implementation for saving the machine settings into the given
 
8143
 * settings::MachineConfigFile instance. This copies machine extradata
 
8144
 * from the previous machine config file in the instance data, if any.
 
8145
 *
 
8146
 * This gets called from two locations:
 
8147
 *
 
8148
 *  --  Machine::saveSettings(), during the regular XML writing;
 
8149
 *
 
8150
 *  --  Appliance::buildXMLForOneVirtualSystem(), when a machine gets
 
8151
 *      exported to OVF and we write the VirtualBox proprietary XML
 
8152
 *      into a <vbox:Machine> tag.
 
8153
 *
 
8154
 * This routine fills all the fields in there, including snapshots, *except*
 
8155
 * for the following:
 
8156
 *
 
8157
 * -- fCurrentStateModified. There is some special logic associated with that.
 
8158
 *
 
8159
 * The caller can then call MachineConfigFile::write() or do something else
 
8160
 * with it.
 
8161
 *
 
8162
 * Caller must hold the machine lock!
 
8163
 *
 
8164
 * This throws XML errors and HRESULT, so the caller must have a catch block!
 
8165
 */
 
8166
void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
 
8167
{
 
8168
    // deep copy extradata
 
8169
    config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
 
8170
 
 
8171
    config.uuid = mData->mUuid;
 
8172
 
 
8173
    // copy name, description, OS type, teleport, UTC etc.
 
8174
    config.machineUserData = mUserData->s;
 
8175
 
 
8176
    if (    mData->mMachineState == MachineState_Saved
 
8177
         || mData->mMachineState == MachineState_Restoring
 
8178
            // when deleting a snapshot we may or may not have a saved state in the current state,
 
8179
            // so let's not assert here please
 
8180
         || (    (   mData->mMachineState == MachineState_DeletingSnapshot
 
8181
                  || mData->mMachineState == MachineState_DeletingSnapshotOnline
 
8182
                  || mData->mMachineState == MachineState_DeletingSnapshotPaused)
 
8183
              && (!mSSData->mStateFilePath.isEmpty())
 
8184
            )
 
8185
        )
 
8186
    {
 
8187
        Assert(!mSSData->mStateFilePath.isEmpty());
 
8188
        /* try to make the file name relative to the settings file dir */
 
8189
        copyPathRelativeToMachine(mSSData->mStateFilePath, config.strStateFile);
 
8190
    }
 
8191
    else
 
8192
    {
 
8193
        Assert(mSSData->mStateFilePath.isEmpty());
 
8194
        config.strStateFile.setNull();
 
8195
    }
 
8196
 
 
8197
    if (mData->mCurrentSnapshot)
 
8198
        config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
 
8199
    else
 
8200
        config.uuidCurrentSnapshot.clear();
 
8201
 
 
8202
    config.timeLastStateChange = mData->mLastStateChange;
 
8203
    config.fAborted = (mData->mMachineState == MachineState_Aborted);
 
8204
    /// @todo Live Migration:        config.fTeleported = (mData->mMachineState == MachineState_Teleported);
 
8205
 
 
8206
    HRESULT rc = saveHardware(config.hardwareMachine);
 
8207
    if (FAILED(rc)) throw rc;
 
8208
 
 
8209
    rc = saveStorageControllers(config.storageMachine);
 
8210
    if (FAILED(rc)) throw rc;
 
8211
 
 
8212
    // save machine's media registry if this is VirtualBox 4.0 or later
 
8213
    if (config.canHaveOwnMediaRegistry())
 
8214
    {
 
8215
        // determine machine folder
 
8216
        Utf8Str strMachineFolder = getSettingsFileFull();
 
8217
        strMachineFolder.stripFilename();
 
8218
        mParent->saveMediaRegistry(config.mediaRegistry,
 
8219
                                   getId(),             // only media with registry ID == machine UUID
 
8220
                                   strMachineFolder);
 
8221
            // this throws HRESULT
 
8222
    }
 
8223
 
 
8224
    // save snapshots
 
8225
    rc = saveAllSnapshots(config);
 
8226
    if (FAILED(rc)) throw rc;
 
8227
}
 
8228
 
 
8229
/**
 
8230
 * Saves all snapshots of the machine into the given machine config file. Called
 
8231
 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
 
8232
 * @param config
 
8233
 * @return
 
8234
 */
 
8235
HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
 
8236
{
 
8237
    AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
 
8238
 
 
8239
    HRESULT rc = S_OK;
 
8240
 
 
8241
    try
 
8242
    {
 
8243
        config.llFirstSnapshot.clear();
 
8244
 
 
8245
        if (mData->mFirstSnapshot)
 
8246
        {
 
8247
            settings::Snapshot snapNew;
 
8248
            config.llFirstSnapshot.push_back(snapNew);
 
8249
 
 
8250
            // get reference to the fresh copy of the snapshot on the list and
 
8251
            // work on that copy directly to avoid excessive copying later
 
8252
            settings::Snapshot &snap = config.llFirstSnapshot.front();
 
8253
 
 
8254
            rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
 
8255
            if (FAILED(rc)) throw rc;
 
8256
        }
 
8257
 
 
8258
//         if (mType == IsSessionMachine)
 
8259
//             mParent->onMachineDataChange(mData->mUuid);          @todo is this necessary?
 
8260
 
 
8261
    }
 
8262
    catch (HRESULT err)
 
8263
    {
 
8264
        /* we assume that error info is set by the thrower */
 
8265
        rc = err;
 
8266
    }
 
8267
    catch (...)
 
8268
    {
 
8269
        rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
 
8270
    }
 
8271
 
 
8272
    return rc;
 
8273
}
 
8274
 
 
8275
/**
 
8276
 *  Saves the VM hardware configuration. It is assumed that the
 
8277
 *  given node is empty.
 
8278
 *
 
8279
 *  @param aNode    <Hardware> node to save the VM hardware configuration to.
 
8280
 */
 
8281
HRESULT Machine::saveHardware(settings::Hardware &data)
 
8282
{
 
8283
    HRESULT rc = S_OK;
 
8284
 
 
8285
    try
 
8286
    {
 
8287
        /* The hardware version attribute (optional).
 
8288
            Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
 
8289
        if (    mHWData->mHWVersion == "1"
 
8290
             && mSSData->mStateFilePath.isEmpty()
 
8291
           )
 
8292
            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. */
 
8293
 
 
8294
        data.strVersion = mHWData->mHWVersion;
 
8295
        data.uuid = mHWData->mHardwareUUID;
 
8296
 
 
8297
        // CPU
 
8298
        data.fHardwareVirt          = !!mHWData->mHWVirtExEnabled;
 
8299
        data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
 
8300
        data.fNestedPaging          = !!mHWData->mHWVirtExNestedPagingEnabled;
 
8301
        data.fLargePages            = !!mHWData->mHWVirtExLargePagesEnabled;
 
8302
        data.fVPID                  = !!mHWData->mHWVirtExVPIDEnabled;
 
8303
        data.fHardwareVirtForce     = !!mHWData->mHWVirtExForceEnabled;
 
8304
        data.fPAE                   = !!mHWData->mPAEEnabled;
 
8305
        data.fSyntheticCpu          = !!mHWData->mSyntheticCpu;
 
8306
 
 
8307
        /* Standard and Extended CPUID leafs. */
 
8308
        data.llCpuIdLeafs.clear();
 
8309
        for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
 
8310
        {
 
8311
            if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
 
8312
                data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
 
8313
        }
 
8314
        for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
 
8315
        {
 
8316
            if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
 
8317
                data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
 
8318
        }
 
8319
 
 
8320
        data.cCPUs             = mHWData->mCPUCount;
 
8321
        data.fCpuHotPlug       = !!mHWData->mCPUHotPlugEnabled;
 
8322
        data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
 
8323
 
 
8324
        data.llCpus.clear();
 
8325
        if (data.fCpuHotPlug)
 
8326
        {
 
8327
            for (unsigned idx = 0; idx < data.cCPUs; idx++)
 
8328
            {
 
8329
                if (mHWData->mCPUAttached[idx])
 
8330
                {
 
8331
                    settings::Cpu cpu;
 
8332
                    cpu.ulId = idx;
 
8333
                    data.llCpus.push_back(cpu);
 
8334
                }
 
8335
            }
 
8336
        }
 
8337
 
 
8338
        // memory
 
8339
        data.ulMemorySizeMB = mHWData->mMemorySize;
 
8340
        data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
 
8341
 
 
8342
        // firmware
 
8343
        data.firmwareType = mHWData->mFirmwareType;
 
8344
 
 
8345
        // HID
 
8346
        data.pointingHidType = mHWData->mPointingHidType;
 
8347
        data.keyboardHidType = mHWData->mKeyboardHidType;
 
8348
 
 
8349
        // chipset
 
8350
        data.chipsetType = mHWData->mChipsetType;
 
8351
 
 
8352
        // HPET
 
8353
        data.fHpetEnabled = !!mHWData->mHpetEnabled;
 
8354
 
 
8355
        // boot order
 
8356
        data.mapBootOrder.clear();
 
8357
        for (size_t i = 0;
 
8358
             i < RT_ELEMENTS(mHWData->mBootOrder);
 
8359
             ++i)
 
8360
            data.mapBootOrder[i] = mHWData->mBootOrder[i];
 
8361
 
 
8362
        // display
 
8363
        data.ulVRAMSizeMB = mHWData->mVRAMSize;
 
8364
        data.cMonitors = mHWData->mMonitorCount;
 
8365
        data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
 
8366
        data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
 
8367
 
 
8368
        /* VRDEServer settings (optional) */
 
8369
        rc = mVRDEServer->saveSettings(data.vrdeSettings);
 
8370
        if (FAILED(rc)) throw rc;
 
8371
 
 
8372
        /* BIOS (required) */
 
8373
        rc = mBIOSSettings->saveSettings(data.biosSettings);
 
8374
        if (FAILED(rc)) throw rc;
 
8375
 
 
8376
        /* USB Controller (required) */
 
8377
        rc = mUSBController->saveSettings(data.usbController);
 
8378
        if (FAILED(rc)) throw rc;
 
8379
 
 
8380
        /* Network adapters (required) */
 
8381
        data.llNetworkAdapters.clear();
 
8382
        for (ULONG slot = 0;
 
8383
             slot < RT_ELEMENTS(mNetworkAdapters);
 
8384
             ++slot)
 
8385
        {
 
8386
            settings::NetworkAdapter nic;
 
8387
            nic.ulSlot = slot;
 
8388
            rc = mNetworkAdapters[slot]->saveSettings(nic);
 
8389
            if (FAILED(rc)) throw rc;
 
8390
 
 
8391
            data.llNetworkAdapters.push_back(nic);
 
8392
        }
 
8393
 
 
8394
        /* Serial ports */
 
8395
        data.llSerialPorts.clear();
 
8396
        for (ULONG slot = 0;
 
8397
             slot < RT_ELEMENTS(mSerialPorts);
 
8398
             ++slot)
 
8399
        {
 
8400
            settings::SerialPort s;
 
8401
            s.ulSlot = slot;
 
8402
            rc = mSerialPorts[slot]->saveSettings(s);
 
8403
            if (FAILED(rc)) return rc;
 
8404
 
 
8405
            data.llSerialPorts.push_back(s);
 
8406
        }
 
8407
 
 
8408
        /* Parallel ports */
 
8409
        data.llParallelPorts.clear();
 
8410
        for (ULONG slot = 0;
 
8411
             slot < RT_ELEMENTS(mParallelPorts);
 
8412
             ++slot)
 
8413
        {
 
8414
            settings::ParallelPort p;
 
8415
            p.ulSlot = slot;
 
8416
            rc = mParallelPorts[slot]->saveSettings(p);
 
8417
            if (FAILED(rc)) return rc;
 
8418
 
 
8419
            data.llParallelPorts.push_back(p);
 
8420
        }
 
8421
 
 
8422
        /* Audio adapter */
 
8423
        rc = mAudioAdapter->saveSettings(data.audioAdapter);
 
8424
        if (FAILED(rc)) return rc;
 
8425
 
 
8426
        /* Shared folders */
 
8427
        data.llSharedFolders.clear();
 
8428
        for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
 
8429
            it != mHWData->mSharedFolders.end();
 
8430
            ++it)
 
8431
        {
 
8432
            ComObjPtr<SharedFolder> pFolder = *it;
 
8433
            settings::SharedFolder sf;
 
8434
            sf.strName = pFolder->getName();
 
8435
            sf.strHostPath = pFolder->getHostPath();
 
8436
            sf.fWritable = !!pFolder->isWritable();
 
8437
            sf.fAutoMount = !!pFolder->isAutoMounted();
 
8438
 
 
8439
            data.llSharedFolders.push_back(sf);
 
8440
        }
 
8441
 
 
8442
        // clipboard
 
8443
        data.clipboardMode = mHWData->mClipboardMode;
 
8444
 
 
8445
        /* Guest */
 
8446
        data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
 
8447
 
 
8448
        // IO settings
 
8449
        data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
 
8450
        data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
 
8451
 
 
8452
        /* BandwidthControl (required) */
 
8453
        rc = mBandwidthControl->saveSettings(data.ioSettings);
 
8454
        if (FAILED(rc)) throw rc;
 
8455
 
 
8456
        // guest properties
 
8457
        data.llGuestProperties.clear();
 
8458
#ifdef VBOX_WITH_GUEST_PROPS
 
8459
        for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
 
8460
             it != mHWData->mGuestProperties.end();
 
8461
             ++it)
 
8462
        {
 
8463
            HWData::GuestProperty property = *it;
 
8464
 
 
8465
            /* Remove transient guest properties at shutdown unless we
 
8466
             * are saving state */
 
8467
            if (   (   mData->mMachineState == MachineState_PoweredOff
 
8468
                    || mData->mMachineState == MachineState_Aborted
 
8469
                    || mData->mMachineState == MachineState_Teleported)
 
8470
                && property.mFlags & guestProp::TRANSIENT)
 
8471
                continue;
 
8472
            settings::GuestProperty prop;
 
8473
            prop.strName = property.strName;
 
8474
            prop.strValue = property.strValue;
 
8475
            prop.timestamp = property.mTimestamp;
 
8476
            char szFlags[guestProp::MAX_FLAGS_LEN + 1];
 
8477
            guestProp::writeFlags(property.mFlags, szFlags);
 
8478
            prop.strFlags = szFlags;
 
8479
 
 
8480
            data.llGuestProperties.push_back(prop);
 
8481
        }
 
8482
 
 
8483
        data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
 
8484
        /* I presume this doesn't require a backup(). */
 
8485
        mData->mGuestPropertiesModified = FALSE;
 
8486
#endif /* VBOX_WITH_GUEST_PROPS defined */
 
8487
    }
 
8488
    catch(std::bad_alloc &)
 
8489
    {
 
8490
        return E_OUTOFMEMORY;
 
8491
    }
 
8492
 
 
8493
    AssertComRC(rc);
 
8494
    return rc;
 
8495
}
 
8496
 
 
8497
/**
 
8498
 *  Saves the storage controller configuration.
 
8499
 *
 
8500
 *  @param aNode    <StorageControllers> node to save the VM hardware configuration to.
 
8501
 */
 
8502
HRESULT Machine::saveStorageControllers(settings::Storage &data)
 
8503
{
 
8504
    data.llStorageControllers.clear();
 
8505
 
 
8506
    for (StorageControllerList::const_iterator it = mStorageControllers->begin();
 
8507
         it != mStorageControllers->end();
 
8508
         ++it)
 
8509
    {
 
8510
        HRESULT rc;
 
8511
        ComObjPtr<StorageController> pCtl = *it;
 
8512
 
 
8513
        settings::StorageController ctl;
 
8514
        ctl.strName = pCtl->getName();
 
8515
        ctl.controllerType = pCtl->getControllerType();
 
8516
        ctl.storageBus = pCtl->getStorageBus();
 
8517
        ctl.ulInstance = pCtl->getInstance();
 
8518
        ctl.fBootable = pCtl->getBootable();
 
8519
 
 
8520
        /* Save the port count. */
 
8521
        ULONG portCount;
 
8522
        rc = pCtl->COMGETTER(PortCount)(&portCount);
 
8523
        ComAssertComRCRet(rc, rc);
 
8524
        ctl.ulPortCount = portCount;
 
8525
 
 
8526
        /* Save fUseHostIOCache */
 
8527
        BOOL fUseHostIOCache;
 
8528
        rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
 
8529
        ComAssertComRCRet(rc, rc);
 
8530
        ctl.fUseHostIOCache = !!fUseHostIOCache;
 
8531
 
 
8532
        /* Save IDE emulation settings. */
 
8533
        if (ctl.controllerType == StorageControllerType_IntelAhci)
 
8534
        {
 
8535
            if (    (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
 
8536
                 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
 
8537
                 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
 
8538
                 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
 
8539
               )
 
8540
                ComAssertComRCRet(rc, rc);
 
8541
        }
 
8542
 
 
8543
        /* save the devices now. */
 
8544
        rc = saveStorageDevices(pCtl, ctl);
 
8545
        ComAssertComRCRet(rc, rc);
 
8546
 
 
8547
        data.llStorageControllers.push_back(ctl);
 
8548
    }
 
8549
 
 
8550
    return S_OK;
 
8551
}
 
8552
 
 
8553
/**
 
8554
 *  Saves the hard disk configuration.
 
8555
 */
 
8556
HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
 
8557
                                    settings::StorageController &data)
 
8558
{
 
8559
    MediaData::AttachmentList atts;
 
8560
 
 
8561
    HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
 
8562
    if (FAILED(rc)) return rc;
 
8563
 
 
8564
    data.llAttachedDevices.clear();
 
8565
    for (MediaData::AttachmentList::const_iterator it = atts.begin();
 
8566
         it != atts.end();
 
8567
         ++it)
 
8568
    {
 
8569
        settings::AttachedDevice dev;
 
8570
 
 
8571
        MediumAttachment *pAttach = *it;
 
8572
        Medium *pMedium = pAttach->getMedium();
 
8573
        BandwidthGroup *pBwGroup = pAttach->getBandwidthGroup();
 
8574
 
 
8575
        dev.deviceType = pAttach->getType();
 
8576
        dev.lPort = pAttach->getPort();
 
8577
        dev.lDevice = pAttach->getDevice();
 
8578
        if (pMedium)
 
8579
        {
 
8580
            if (pMedium->isHostDrive())
 
8581
                dev.strHostDriveSrc = pMedium->getLocationFull();
 
8582
            else
 
8583
                dev.uuid = pMedium->getId();
 
8584
            dev.fPassThrough = pAttach->getPassthrough();
 
8585
        }
 
8586
 
 
8587
        if (pBwGroup)
 
8588
        {
 
8589
            dev.strBwGroup = pBwGroup->getName();
 
8590
        }
 
8591
 
 
8592
        data.llAttachedDevices.push_back(dev);
 
8593
    }
 
8594
 
 
8595
    return S_OK;
 
8596
}
 
8597
 
 
8598
/**
 
8599
 *  Saves machine state settings as defined by aFlags
 
8600
 *  (SaveSTS_* values).
 
8601
 *
 
8602
 *  @param aFlags   Combination of SaveSTS_* flags.
 
8603
 *
 
8604
 *  @note Locks objects for writing.
 
8605
 */
 
8606
HRESULT Machine::saveStateSettings(int aFlags)
 
8607
{
 
8608
    if (aFlags == 0)
 
8609
        return S_OK;
 
8610
 
 
8611
    AutoCaller autoCaller(this);
 
8612
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
8613
 
 
8614
    /* This object's write lock is also necessary to serialize file access
 
8615
     * (prevent concurrent reads and writes) */
 
8616
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
8617
 
 
8618
    HRESULT rc = S_OK;
 
8619
 
 
8620
    Assert(mData->pMachineConfigFile);
 
8621
 
 
8622
    try
 
8623
    {
 
8624
        if (aFlags & SaveSTS_CurStateModified)
 
8625
            mData->pMachineConfigFile->fCurrentStateModified = true;
 
8626
 
 
8627
        if (aFlags & SaveSTS_StateFilePath)
 
8628
        {
 
8629
            if (!mSSData->mStateFilePath.isEmpty())
 
8630
                /* try to make the file name relative to the settings file dir */
 
8631
                copyPathRelativeToMachine(mSSData->mStateFilePath, mData->pMachineConfigFile->strStateFile);
 
8632
            else
 
8633
                mData->pMachineConfigFile->strStateFile.setNull();
 
8634
        }
 
8635
 
 
8636
        if (aFlags & SaveSTS_StateTimeStamp)
 
8637
        {
 
8638
            Assert(    mData->mMachineState != MachineState_Aborted
 
8639
                    || mSSData->mStateFilePath.isEmpty());
 
8640
 
 
8641
            mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
 
8642
 
 
8643
            mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
 
8644
//@todo live migration             mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
 
8645
        }
 
8646
 
 
8647
        mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
 
8648
    }
 
8649
    catch (...)
 
8650
    {
 
8651
        rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
 
8652
    }
 
8653
 
 
8654
    return rc;
 
8655
}
 
8656
 
 
8657
/**
 
8658
 * Creates differencing hard disks for all normal hard disks attached to this
 
8659
 * machine and a new set of attachments to refer to created disks.
 
8660
 *
 
8661
 * Used when taking a snapshot or when deleting the current state. Gets called
 
8662
 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
 
8663
 *
 
8664
 * This method assumes that mMediaData contains the original hard disk attachments
 
8665
 * it needs to create diffs for. On success, these attachments will be replaced
 
8666
 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
 
8667
 * called to delete created diffs which will also rollback mMediaData and restore
 
8668
 * whatever was backed up before calling this method.
 
8669
 *
 
8670
 * Attachments with non-normal hard disks are left as is.
 
8671
 *
 
8672
 * If @a aOnline is @c false then the original hard disks that require implicit
 
8673
 * diffs will be locked for reading. Otherwise it is assumed that they are
 
8674
 * already locked for writing (when the VM was started). Note that in the latter
 
8675
 * case it is responsibility of the caller to lock the newly created diffs for
 
8676
 * writing if this method succeeds.
 
8677
 *
 
8678
 * @param aProgress         Progress object to run (must contain at least as
 
8679
 *                          many operations left as the number of hard disks
 
8680
 *                          attached).
 
8681
 * @param aOnline           Whether the VM was online prior to this operation.
 
8682
 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs to receive the registry IDs that need saving
 
8683
 *
 
8684
 * @note The progress object is not marked as completed, neither on success nor
 
8685
 *       on failure. This is a responsibility of the caller.
 
8686
 *
 
8687
 * @note Locks this object for writing.
 
8688
 */
 
8689
HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
 
8690
                                     ULONG aWeight,
 
8691
                                     bool aOnline,
 
8692
                                     GuidList *pllRegistriesThatNeedSaving)
 
8693
{
 
8694
    LogFlowThisFunc(("aOnline=%d\n", aOnline));
 
8695
 
 
8696
    AutoCaller autoCaller(this);
 
8697
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
8698
 
 
8699
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
8700
 
 
8701
    /* must be in a protective state because we leave the lock below */
 
8702
    AssertReturn(   mData->mMachineState == MachineState_Saving
 
8703
                 || mData->mMachineState == MachineState_LiveSnapshotting
 
8704
                 || mData->mMachineState == MachineState_RestoringSnapshot
 
8705
                 || mData->mMachineState == MachineState_DeletingSnapshot
 
8706
                 , E_FAIL);
 
8707
 
 
8708
    HRESULT rc = S_OK;
 
8709
 
 
8710
    MediumLockListMap lockedMediaOffline;
 
8711
    MediumLockListMap *lockedMediaMap;
 
8712
    if (aOnline)
 
8713
        lockedMediaMap = &mData->mSession.mLockedMedia;
 
8714
    else
 
8715
        lockedMediaMap = &lockedMediaOffline;
 
8716
 
 
8717
    try
 
8718
    {
 
8719
        if (!aOnline)
 
8720
        {
 
8721
            /* lock all attached hard disks early to detect "in use"
 
8722
             * situations before creating actual diffs */
 
8723
            for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
 
8724
                 it != mMediaData->mAttachments.end();
 
8725
                 ++it)
 
8726
            {
 
8727
                MediumAttachment* pAtt = *it;
 
8728
                if (pAtt->getType() == DeviceType_HardDisk)
 
8729
                {
 
8730
                    Medium* pMedium = pAtt->getMedium();
 
8731
                    Assert(pMedium);
 
8732
 
 
8733
                    MediumLockList *pMediumLockList(new MediumLockList());
 
8734
                    rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
 
8735
                                                       false /* fMediumLockWrite */,
 
8736
                                                       NULL,
 
8737
                                                       *pMediumLockList);
 
8738
                    if (FAILED(rc))
 
8739
                    {
 
8740
                        delete pMediumLockList;
 
8741
                        throw rc;
 
8742
                    }
 
8743
                    rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
 
8744
                    if (FAILED(rc))
 
8745
                    {
 
8746
                        throw setError(rc,
 
8747
                                       tr("Collecting locking information for all attached media failed"));
 
8748
                    }
 
8749
                }
 
8750
            }
 
8751
 
 
8752
            /* Now lock all media. If this fails, nothing is locked. */
 
8753
            rc = lockedMediaMap->Lock();
 
8754
            if (FAILED(rc))
 
8755
            {
 
8756
                throw setError(rc,
 
8757
                               tr("Locking of attached media failed"));
 
8758
            }
 
8759
        }
 
8760
 
 
8761
        /* remember the current list (note that we don't use backup() since
 
8762
         * mMediaData may be already backed up) */
 
8763
        MediaData::AttachmentList atts = mMediaData->mAttachments;
 
8764
 
 
8765
        /* start from scratch */
 
8766
        mMediaData->mAttachments.clear();
 
8767
 
 
8768
        /* go through remembered attachments and create diffs for normal hard
 
8769
         * disks and attach them */
 
8770
        for (MediaData::AttachmentList::const_iterator it = atts.begin();
 
8771
             it != atts.end();
 
8772
             ++it)
 
8773
        {
 
8774
            MediumAttachment* pAtt = *it;
 
8775
 
 
8776
            DeviceType_T devType = pAtt->getType();
 
8777
            Medium* pMedium = pAtt->getMedium();
 
8778
 
 
8779
            if (   devType != DeviceType_HardDisk
 
8780
                || pMedium == NULL
 
8781
                || pMedium->getType() != MediumType_Normal)
 
8782
            {
 
8783
                /* copy the attachment as is */
 
8784
 
 
8785
                /** @todo the progress object created in Console::TakeSnaphot
 
8786
                 * only expects operations for hard disks. Later other
 
8787
                 * device types need to show up in the progress as well. */
 
8788
                if (devType == DeviceType_HardDisk)
 
8789
                {
 
8790
                    if (pMedium == NULL)
 
8791
                        aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
 
8792
                                                    aWeight);        // weight
 
8793
                    else
 
8794
                        aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
 
8795
                                                            pMedium->getBase()->getName().c_str()).raw(),
 
8796
                                                    aWeight);        // weight
 
8797
                }
 
8798
 
 
8799
                mMediaData->mAttachments.push_back(pAtt);
 
8800
                continue;
 
8801
            }
 
8802
 
 
8803
            /* need a diff */
 
8804
            aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
 
8805
                                                pMedium->getBase()->getName().c_str()).raw(),
 
8806
                                        aWeight);        // weight
 
8807
 
 
8808
            Utf8Str strFullSnapshotFolder;
 
8809
            calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
 
8810
 
 
8811
            ComObjPtr<Medium> diff;
 
8812
            diff.createObject();
 
8813
            rc = diff->init(mParent,
 
8814
                            pMedium->getPreferredDiffFormat(),
 
8815
                            strFullSnapshotFolder.append(RTPATH_SLASH_STR),
 
8816
                            pMedium->getFirstRegistryMachineId(),        // store the diff in the same registry as the parent
 
8817
                            pllRegistriesThatNeedSaving);
 
8818
            if (FAILED(rc)) throw rc;
 
8819
 
 
8820
            /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
 
8821
             *        the push_back?  Looks like we're going to leave medium with the
 
8822
             *        wrong kind of lock (general issue with if we fail anywhere at all)
 
8823
             *        and an orphaned VDI in the snapshots folder. */
 
8824
 
 
8825
            /* update the appropriate lock list */
 
8826
            MediumLockList *pMediumLockList;
 
8827
            rc = lockedMediaMap->Get(pAtt, pMediumLockList);
 
8828
            AssertComRCThrowRC(rc);
 
8829
            if (aOnline)
 
8830
            {
 
8831
                rc = pMediumLockList->Update(pMedium, false);
 
8832
                AssertComRCThrowRC(rc);
 
8833
            }
 
8834
 
 
8835
            /* leave the lock before the potentially lengthy operation */
 
8836
            alock.leave();
 
8837
            rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
 
8838
                                            pMediumLockList,
 
8839
                                            NULL /* aProgress */,
 
8840
                                            true /* aWait */,
 
8841
                                            pllRegistriesThatNeedSaving);
 
8842
            alock.enter();
 
8843
            if (FAILED(rc)) throw rc;
 
8844
 
 
8845
            rc = lockedMediaMap->Unlock();
 
8846
            AssertComRCThrowRC(rc);
 
8847
            rc = pMediumLockList->Append(diff, true);
 
8848
            AssertComRCThrowRC(rc);
 
8849
            rc = lockedMediaMap->Lock();
 
8850
            AssertComRCThrowRC(rc);
 
8851
 
 
8852
            rc = diff->addBackReference(mData->mUuid);
 
8853
            AssertComRCThrowRC(rc);
 
8854
 
 
8855
            /* add a new attachment */
 
8856
            ComObjPtr<MediumAttachment> attachment;
 
8857
            attachment.createObject();
 
8858
            rc = attachment->init(this,
 
8859
                                  diff,
 
8860
                                  pAtt->getControllerName(),
 
8861
                                  pAtt->getPort(),
 
8862
                                  pAtt->getDevice(),
 
8863
                                  DeviceType_HardDisk,
 
8864
                                  true /* aImplicit */,
 
8865
                                  pAtt->getBandwidthGroup());
 
8866
            if (FAILED(rc)) throw rc;
 
8867
 
 
8868
            rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
 
8869
            AssertComRCThrowRC(rc);
 
8870
            mMediaData->mAttachments.push_back(attachment);
 
8871
        }
 
8872
    }
 
8873
    catch (HRESULT aRC) { rc = aRC; }
 
8874
 
 
8875
    /* unlock all hard disks we locked */
 
8876
    if (!aOnline)
 
8877
    {
 
8878
        ErrorInfoKeeper eik;
 
8879
 
 
8880
        rc = lockedMediaMap->Clear();
 
8881
        AssertComRC(rc);
 
8882
    }
 
8883
 
 
8884
    if (FAILED(rc))
 
8885
    {
 
8886
        MultiResult mrc = rc;
 
8887
 
 
8888
        mrc = deleteImplicitDiffs(pllRegistriesThatNeedSaving);
 
8889
    }
 
8890
 
 
8891
    return rc;
 
8892
}
 
8893
 
 
8894
/**
 
8895
 * Deletes implicit differencing hard disks created either by
 
8896
 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
 
8897
 *
 
8898
 * Note that to delete hard disks created by #AttachMedium() this method is
 
8899
 * called from #fixupMedia() when the changes are rolled back.
 
8900
 *
 
8901
 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
 
8902
 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
 
8903
 *
 
8904
 * @note Locks this object for writing.
 
8905
 */
 
8906
HRESULT Machine::deleteImplicitDiffs(GuidList *pllRegistriesThatNeedSaving)
 
8907
{
 
8908
    AutoCaller autoCaller(this);
 
8909
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
8910
 
 
8911
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
8912
    LogFlowThisFuncEnter();
 
8913
 
 
8914
    AssertReturn(mMediaData.isBackedUp(), E_FAIL);
 
8915
 
 
8916
    HRESULT rc = S_OK;
 
8917
 
 
8918
    MediaData::AttachmentList implicitAtts;
 
8919
 
 
8920
    const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
 
8921
 
 
8922
    /* enumerate new attachments */
 
8923
    for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
 
8924
         it != mMediaData->mAttachments.end();
 
8925
         ++it)
 
8926
    {
 
8927
        ComObjPtr<Medium> hd = (*it)->getMedium();
 
8928
        if (hd.isNull())
 
8929
            continue;
 
8930
 
 
8931
        if ((*it)->isImplicit())
 
8932
        {
 
8933
            /* deassociate and mark for deletion */
 
8934
            LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
 
8935
            rc = hd->removeBackReference(mData->mUuid);
 
8936
            AssertComRC(rc);
 
8937
            implicitAtts.push_back(*it);
 
8938
            continue;
 
8939
        }
 
8940
 
 
8941
        /* was this hard disk attached before? */
 
8942
        if (!findAttachment(oldAtts, hd))
 
8943
        {
 
8944
            /* no: de-associate */
 
8945
            LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
 
8946
            rc = hd->removeBackReference(mData->mUuid);
 
8947
            AssertComRC(rc);
 
8948
            continue;
 
8949
        }
 
8950
        LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
 
8951
    }
 
8952
 
 
8953
    /* rollback hard disk changes */
 
8954
    mMediaData.rollback();
 
8955
 
 
8956
    MultiResult mrc(S_OK);
 
8957
 
 
8958
    /* delete unused implicit diffs */
 
8959
    if (implicitAtts.size() != 0)
 
8960
    {
 
8961
        /* will leave the lock before the potentially lengthy
 
8962
         * operation, so protect with the special state (unless already
 
8963
         * protected) */
 
8964
        MachineState_T oldState = mData->mMachineState;
 
8965
        if (    oldState != MachineState_Saving
 
8966
             && oldState != MachineState_LiveSnapshotting
 
8967
             && oldState != MachineState_RestoringSnapshot
 
8968
             && oldState != MachineState_DeletingSnapshot
 
8969
             && oldState != MachineState_DeletingSnapshotOnline
 
8970
             && oldState != MachineState_DeletingSnapshotPaused
 
8971
           )
 
8972
            setMachineState(MachineState_SettingUp);
 
8973
 
 
8974
        alock.leave();
 
8975
 
 
8976
        for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
 
8977
             it != implicitAtts.end();
 
8978
             ++it)
 
8979
        {
 
8980
            LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
 
8981
            ComObjPtr<Medium> hd = (*it)->getMedium();
 
8982
 
 
8983
            rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
 
8984
                                   pllRegistriesThatNeedSaving);
 
8985
            AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
 
8986
            mrc = rc;
 
8987
        }
 
8988
 
 
8989
        alock.enter();
 
8990
 
 
8991
        if (mData->mMachineState == MachineState_SettingUp)
 
8992
            setMachineState(oldState);
 
8993
    }
 
8994
 
 
8995
    return mrc;
 
8996
}
 
8997
 
 
8998
/**
 
8999
 * Looks through the given list of media attachments for one with the given parameters
 
9000
 * and returns it, or NULL if not found. The list is a parameter so that backup lists
 
9001
 * can be searched as well if needed.
 
9002
 *
 
9003
 * @param list
 
9004
 * @param aControllerName
 
9005
 * @param aControllerPort
 
9006
 * @param aDevice
 
9007
 * @return
 
9008
 */
 
9009
MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
 
9010
                                          IN_BSTR aControllerName,
 
9011
                                          LONG aControllerPort,
 
9012
                                          LONG aDevice)
 
9013
{
 
9014
   for (MediaData::AttachmentList::const_iterator it = ll.begin();
 
9015
        it != ll.end();
 
9016
        ++it)
 
9017
    {
 
9018
        MediumAttachment *pAttach = *it;
 
9019
        if (pAttach->matches(aControllerName, aControllerPort, aDevice))
 
9020
            return pAttach;
 
9021
    }
 
9022
 
 
9023
    return NULL;
 
9024
}
 
9025
 
 
9026
/**
 
9027
 * Looks through the given list of media attachments for one with the given parameters
 
9028
 * and returns it, or NULL if not found. The list is a parameter so that backup lists
 
9029
 * can be searched as well if needed.
 
9030
 *
 
9031
 * @param list
 
9032
 * @param aControllerName
 
9033
 * @param aControllerPort
 
9034
 * @param aDevice
 
9035
 * @return
 
9036
 */
 
9037
MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
 
9038
                                          ComObjPtr<Medium> pMedium)
 
9039
{
 
9040
   for (MediaData::AttachmentList::const_iterator it = ll.begin();
 
9041
        it != ll.end();
 
9042
        ++it)
 
9043
    {
 
9044
        MediumAttachment *pAttach = *it;
 
9045
        ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
 
9046
        if (pMediumThis == pMedium)
 
9047
            return pAttach;
 
9048
    }
 
9049
 
 
9050
    return NULL;
 
9051
}
 
9052
 
 
9053
/**
 
9054
 * Looks through the given list of media attachments for one with the given parameters
 
9055
 * and returns it, or NULL if not found. The list is a parameter so that backup lists
 
9056
 * can be searched as well if needed.
 
9057
 *
 
9058
 * @param list
 
9059
 * @param aControllerName
 
9060
 * @param aControllerPort
 
9061
 * @param aDevice
 
9062
 * @return
 
9063
 */
 
9064
MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
 
9065
                                          Guid &id)
 
9066
{
 
9067
   for (MediaData::AttachmentList::const_iterator it = ll.begin();
 
9068
         it != ll.end();
 
9069
         ++it)
 
9070
    {
 
9071
        MediumAttachment *pAttach = *it;
 
9072
        ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
 
9073
        if (pMediumThis->getId() == id)
 
9074
            return pAttach;
 
9075
    }
 
9076
 
 
9077
    return NULL;
 
9078
}
 
9079
 
 
9080
/**
 
9081
 * Main implementation for Machine::DetachDevice. This also gets called
 
9082
 * from Machine::prepareUnregister() so it has been taken out for simplicity.
 
9083
 *
 
9084
 * @param pAttach Medium attachment to detach.
 
9085
 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
 
9086
 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
 
9087
 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
 
9088
 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
 
9089
 * @return
 
9090
 */
 
9091
HRESULT Machine::detachDevice(MediumAttachment *pAttach,
 
9092
                              AutoWriteLock &writeLock,
 
9093
                              Snapshot *pSnapshot,
 
9094
                              GuidList *pllRegistriesThatNeedSaving)
 
9095
{
 
9096
    ComObjPtr<Medium> oldmedium = pAttach->getMedium();
 
9097
    DeviceType_T mediumType = pAttach->getType();
 
9098
 
 
9099
    LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
 
9100
 
 
9101
    if (pAttach->isImplicit())
 
9102
    {
 
9103
        /* attempt to implicitly delete the implicitly created diff */
 
9104
 
 
9105
            /// @todo move the implicit flag from MediumAttachment to Medium
 
9106
            /// and forbid any hard disk operation when it is implicit. Or maybe
 
9107
            /// a special media state for it to make it even more simple.
 
9108
 
 
9109
        Assert(mMediaData.isBackedUp());
 
9110
 
 
9111
            /* will leave the lock before the potentially lengthy operation, so
 
9112
            * protect with the special state */
 
9113
        MachineState_T oldState = mData->mMachineState;
 
9114
        setMachineState(MachineState_SettingUp);
 
9115
 
 
9116
        writeLock.release();
 
9117
 
 
9118
        HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
 
9119
                                              pllRegistriesThatNeedSaving);
 
9120
 
 
9121
        writeLock.acquire();
 
9122
 
 
9123
        setMachineState(oldState);
 
9124
 
 
9125
        if (FAILED(rc)) return rc;
 
9126
    }
 
9127
 
 
9128
    setModified(IsModified_Storage);
 
9129
    mMediaData.backup();
 
9130
 
 
9131
    // we cannot use erase (it) below because backup() above will create
 
9132
    // a copy of the list and make this copy active, but the iterator
 
9133
    // still refers to the original and is not valid for the copy
 
9134
    mMediaData->mAttachments.remove(pAttach);
 
9135
 
 
9136
    if (!oldmedium.isNull())
 
9137
    {
 
9138
        // if this is from a snapshot, do not defer detachment to commitMedia()
 
9139
        if (pSnapshot)
 
9140
            oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
 
9141
        // else if non-hard disk media, do not defer detachment to commitMedia() either
 
9142
        else if (mediumType != DeviceType_HardDisk)
 
9143
            oldmedium->removeBackReference(mData->mUuid);
 
9144
    }
 
9145
 
 
9146
    return S_OK;
 
9147
}
 
9148
 
 
9149
/**
 
9150
 * Goes thru all medium attachments of the list and calls detachDevice() on each
 
9151
 * of them and attaches all Medium objects found in the process to the given list,
 
9152
 * depending on cleanupMode.
 
9153
 *
 
9154
 * This gets called from Machine::Unregister, both for the actual Machine and
 
9155
 * the SnapshotMachine objects that might be found in the snapshots.
 
9156
 *
 
9157
 * Requires caller and locking.
 
9158
 *
 
9159
 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
 
9160
 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
 
9161
 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
 
9162
 *          otherwise no media get added.
 
9163
 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
 
9164
 * @return
 
9165
 */
 
9166
HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
 
9167
                                Snapshot *pSnapshot,
 
9168
                                CleanupMode_T cleanupMode,
 
9169
                                MediaList &llMedia)
 
9170
{
 
9171
    Assert(isWriteLockOnCurrentThread());
 
9172
 
 
9173
    HRESULT rc;
 
9174
 
 
9175
    // make a temporary list because detachDevice invalidates iterators into
 
9176
    // mMediaData->mAttachments
 
9177
    MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
 
9178
 
 
9179
    for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
 
9180
         it != llAttachments2.end();
 
9181
         ++it)
 
9182
    {
 
9183
        ComObjPtr<MediumAttachment> pAttach = *it;
 
9184
        ComObjPtr<Medium> pMedium = pAttach->getMedium();
 
9185
 
 
9186
        if (!pMedium.isNull())
 
9187
        {
 
9188
            DeviceType_T devType = pMedium->getDeviceType();
 
9189
            if (    (    cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
 
9190
                      && devType == DeviceType_HardDisk)
 
9191
                 || (cleanupMode == CleanupMode_Full)
 
9192
               )
 
9193
                llMedia.push_back(pMedium);
 
9194
        }
 
9195
 
 
9196
        // real machine: then we need to use the proper method
 
9197
        rc = detachDevice(pAttach,
 
9198
                          writeLock,
 
9199
                          pSnapshot,
 
9200
                          NULL /* pfNeedsSaveSettings */);
 
9201
 
 
9202
        if (FAILED(rc))
 
9203
            return rc;
 
9204
    }
 
9205
 
 
9206
    return S_OK;
 
9207
}
 
9208
 
 
9209
/**
 
9210
 * Perform deferred hard disk detachments.
 
9211
 *
 
9212
 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
 
9213
 * backed up).
 
9214
 *
 
9215
 * If @a aOnline is @c true then this method will also unlock the old hard disks
 
9216
 * for which the new implicit diffs were created and will lock these new diffs for
 
9217
 * writing.
 
9218
 *
 
9219
 * @param aOnline       Whether the VM was online prior to this operation.
 
9220
 *
 
9221
 * @note Locks this object for writing!
 
9222
 */
 
9223
void Machine::commitMedia(bool aOnline /*= false*/)
 
9224
{
 
9225
    AutoCaller autoCaller(this);
 
9226
    AssertComRCReturnVoid(autoCaller.rc());
 
9227
 
 
9228
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
9229
 
 
9230
    LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
 
9231
 
 
9232
    HRESULT rc = S_OK;
 
9233
 
 
9234
    /* no attach/detach operations -- nothing to do */
 
9235
    if (!mMediaData.isBackedUp())
 
9236
        return;
 
9237
 
 
9238
    MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
 
9239
    bool fMediaNeedsLocking = false;
 
9240
 
 
9241
    /* enumerate new attachments */
 
9242
    for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
 
9243
         it != mMediaData->mAttachments.end();
 
9244
         ++it)
 
9245
    {
 
9246
        MediumAttachment *pAttach = *it;
 
9247
 
 
9248
        pAttach->commit();
 
9249
 
 
9250
        Medium* pMedium = pAttach->getMedium();
 
9251
        bool fImplicit = pAttach->isImplicit();
 
9252
 
 
9253
        LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
 
9254
                         (pMedium) ? pMedium->getName().c_str() : "NULL",
 
9255
                         fImplicit));
 
9256
 
 
9257
        /** @todo convert all this Machine-based voodoo to MediumAttachment
 
9258
         * based commit logic. */
 
9259
        if (fImplicit)
 
9260
        {
 
9261
            /* convert implicit attachment to normal */
 
9262
            pAttach->setImplicit(false);
 
9263
 
 
9264
            if (    aOnline
 
9265
                 && pMedium
 
9266
                 && pAttach->getType() == DeviceType_HardDisk
 
9267
               )
 
9268
            {
 
9269
                ComObjPtr<Medium> parent = pMedium->getParent();
 
9270
                AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
 
9271
 
 
9272
                /* update the appropriate lock list */
 
9273
                MediumLockList *pMediumLockList;
 
9274
                rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
 
9275
                AssertComRC(rc);
 
9276
                if (pMediumLockList)
 
9277
                {
 
9278
                    /* unlock if there's a need to change the locking */
 
9279
                    if (!fMediaNeedsLocking)
 
9280
                    {
 
9281
                        rc = mData->mSession.mLockedMedia.Unlock();
 
9282
                        AssertComRC(rc);
 
9283
                        fMediaNeedsLocking = true;
 
9284
                    }
 
9285
                    rc = pMediumLockList->Update(parent, false);
 
9286
                    AssertComRC(rc);
 
9287
                    rc = pMediumLockList->Append(pMedium, true);
 
9288
                    AssertComRC(rc);
 
9289
                }
 
9290
            }
 
9291
 
 
9292
            continue;
 
9293
        }
 
9294
 
 
9295
        if (pMedium)
 
9296
        {
 
9297
            /* was this medium attached before? */
 
9298
            for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
 
9299
                 oldIt != oldAtts.end();
 
9300
                 ++oldIt)
 
9301
            {
 
9302
                MediumAttachment *pOldAttach = *oldIt;
 
9303
                if (pOldAttach->getMedium() == pMedium)
 
9304
                {
 
9305
                    LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
 
9306
 
 
9307
                    /* yes: remove from old to avoid de-association */
 
9308
                    oldAtts.erase(oldIt);
 
9309
                    break;
 
9310
                }
 
9311
            }
 
9312
        }
 
9313
    }
 
9314
 
 
9315
    /* enumerate remaining old attachments and de-associate from the
 
9316
     * current machine state */
 
9317
    for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
 
9318
         it != oldAtts.end();
 
9319
         ++it)
 
9320
    {
 
9321
        MediumAttachment *pAttach = *it;
 
9322
        Medium* pMedium = pAttach->getMedium();
 
9323
 
 
9324
        /* Detach only hard disks, since DVD/floppy media is detached
 
9325
         * instantly in MountMedium. */
 
9326
        if (pAttach->getType() == DeviceType_HardDisk && pMedium)
 
9327
        {
 
9328
            LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
 
9329
 
 
9330
            /* now de-associate from the current machine state */
 
9331
            rc = pMedium->removeBackReference(mData->mUuid);
 
9332
            AssertComRC(rc);
 
9333
 
 
9334
            if (aOnline)
 
9335
            {
 
9336
                /* unlock since medium is not used anymore */
 
9337
                MediumLockList *pMediumLockList;
 
9338
                rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
 
9339
                AssertComRC(rc);
 
9340
                if (pMediumLockList)
 
9341
                {
 
9342
                    rc = mData->mSession.mLockedMedia.Remove(pAttach);
 
9343
                    AssertComRC(rc);
 
9344
                }
 
9345
            }
 
9346
        }
 
9347
    }
 
9348
 
 
9349
    /* take media locks again so that the locking state is consistent */
 
9350
    if (fMediaNeedsLocking)
 
9351
    {
 
9352
        Assert(aOnline);
 
9353
        rc = mData->mSession.mLockedMedia.Lock();
 
9354
        AssertComRC(rc);
 
9355
    }
 
9356
 
 
9357
    /* commit the hard disk changes */
 
9358
    mMediaData.commit();
 
9359
 
 
9360
    if (isSessionMachine())
 
9361
    {
 
9362
        /* attach new data to the primary machine and reshare it */
 
9363
        mPeer->mMediaData.attach(mMediaData);
 
9364
    }
 
9365
 
 
9366
    return;
 
9367
}
 
9368
 
 
9369
/**
 
9370
 * Perform deferred deletion of implicitly created diffs.
 
9371
 *
 
9372
 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
 
9373
 * backed up).
 
9374
 *
 
9375
 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
 
9376
 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
 
9377
 *
 
9378
 * @note Locks this object for writing!
 
9379
 */
 
9380
void Machine::rollbackMedia()
 
9381
{
 
9382
    AutoCaller autoCaller(this);
 
9383
    AssertComRCReturnVoid (autoCaller.rc());
 
9384
 
 
9385
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
9386
 
 
9387
    LogFlowThisFunc(("Entering\n"));
 
9388
 
 
9389
    HRESULT rc = S_OK;
 
9390
 
 
9391
    /* no attach/detach operations -- nothing to do */
 
9392
    if (!mMediaData.isBackedUp())
 
9393
        return;
 
9394
 
 
9395
    /* enumerate new attachments */
 
9396
    for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
 
9397
         it != mMediaData->mAttachments.end();
 
9398
         ++it)
 
9399
    {
 
9400
        MediumAttachment *pAttach = *it;
 
9401
        /* Fix up the backrefs for DVD/floppy media. */
 
9402
        if (pAttach->getType() != DeviceType_HardDisk)
 
9403
        {
 
9404
            Medium* pMedium = pAttach->getMedium();
 
9405
            if (pMedium)
 
9406
            {
 
9407
                rc = pMedium->removeBackReference(mData->mUuid);
 
9408
                AssertComRC(rc);
 
9409
            }
 
9410
        }
 
9411
 
 
9412
        (*it)->rollback();
 
9413
 
 
9414
        pAttach = *it;
 
9415
        /* Fix up the backrefs for DVD/floppy media. */
 
9416
        if (pAttach->getType() != DeviceType_HardDisk)
 
9417
        {
 
9418
            Medium* pMedium = pAttach->getMedium();
 
9419
            if (pMedium)
 
9420
            {
 
9421
                rc = pMedium->addBackReference(mData->mUuid);
 
9422
                AssertComRC(rc);
 
9423
            }
 
9424
        }
 
9425
    }
 
9426
 
 
9427
    /** @todo convert all this Machine-based voodoo to MediumAttachment
 
9428
     * based rollback logic. */
 
9429
    // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
 
9430
    // which gets called if Machine::registeredInit() fails...
 
9431
    deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);
 
9432
 
 
9433
    return;
 
9434
}
 
9435
 
 
9436
/**
 
9437
 *  Returns true if the settings file is located in the directory named exactly
 
9438
 *  as the machine; this means, among other things, that the machine directory
 
9439
 *  should be auto-renamed.
 
9440
 *
 
9441
 *  @param aSettingsDir if not NULL, the full machine settings file directory
 
9442
 *                      name will be assigned there.
 
9443
 *
 
9444
 *  @note Doesn't lock anything.
 
9445
 *  @note Not thread safe (must be called from this object's lock).
 
9446
 */
 
9447
bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
 
9448
{
 
9449
    Utf8Str strMachineDirName(mData->m_strConfigFileFull);  // path/to/machinesfolder/vmname/vmname.vbox
 
9450
    strMachineDirName.stripFilename();                      // path/to/machinesfolder/vmname
 
9451
    if (aSettingsDir)
 
9452
        *aSettingsDir = strMachineDirName;
 
9453
    strMachineDirName.stripPath();                          // vmname
 
9454
    Utf8Str strConfigFileOnly(mData->m_strConfigFileFull);  // path/to/machinesfolder/vmname/vmname.vbox
 
9455
    strConfigFileOnly.stripPath()                           // vmname.vbox
 
9456
                     .stripExt();                           // vmname
 
9457
 
 
9458
    AssertReturn(!strMachineDirName.isEmpty(), false);
 
9459
    AssertReturn(!strConfigFileOnly.isEmpty(), false);
 
9460
 
 
9461
    return strMachineDirName == strConfigFileOnly;
 
9462
}
 
9463
 
 
9464
/**
 
9465
 * Discards all changes to machine settings.
 
9466
 *
 
9467
 * @param aNotify   Whether to notify the direct session about changes or not.
 
9468
 *
 
9469
 * @note Locks objects for writing!
 
9470
 */
 
9471
void Machine::rollback(bool aNotify)
 
9472
{
 
9473
    AutoCaller autoCaller(this);
 
9474
    AssertComRCReturn(autoCaller.rc(), (void)0);
 
9475
 
 
9476
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
9477
 
 
9478
    if (!mStorageControllers.isNull())
 
9479
    {
 
9480
        if (mStorageControllers.isBackedUp())
 
9481
        {
 
9482
            /* unitialize all new devices (absent in the backed up list). */
 
9483
            StorageControllerList::const_iterator it = mStorageControllers->begin();
 
9484
            StorageControllerList *backedList = mStorageControllers.backedUpData();
 
9485
            while (it != mStorageControllers->end())
 
9486
            {
 
9487
                if (   std::find(backedList->begin(), backedList->end(), *it)
 
9488
                    == backedList->end()
 
9489
                   )
 
9490
                {
 
9491
                    (*it)->uninit();
 
9492
                }
 
9493
                ++it;
 
9494
            }
 
9495
 
 
9496
            /* restore the list */
 
9497
            mStorageControllers.rollback();
 
9498
        }
 
9499
 
 
9500
        /* rollback any changes to devices after restoring the list */
 
9501
        if (mData->flModifications & IsModified_Storage)
 
9502
        {
 
9503
            StorageControllerList::const_iterator it = mStorageControllers->begin();
 
9504
            while (it != mStorageControllers->end())
 
9505
            {
 
9506
                (*it)->rollback();
 
9507
                ++it;
 
9508
            }
 
9509
        }
 
9510
    }
 
9511
 
 
9512
    mUserData.rollback();
 
9513
 
 
9514
    mHWData.rollback();
 
9515
 
 
9516
    if (mData->flModifications & IsModified_Storage)
 
9517
        rollbackMedia();
 
9518
 
 
9519
    if (mBIOSSettings)
 
9520
        mBIOSSettings->rollback();
 
9521
 
 
9522
    if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
 
9523
        mVRDEServer->rollback();
 
9524
 
 
9525
    if (mAudioAdapter)
 
9526
        mAudioAdapter->rollback();
 
9527
 
 
9528
    if (mUSBController && (mData->flModifications & IsModified_USB))
 
9529
        mUSBController->rollback();
 
9530
 
 
9531
    if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
 
9532
        mBandwidthControl->rollback();
 
9533
 
 
9534
    ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)];
 
9535
    ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
 
9536
    ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
 
9537
 
 
9538
    if (mData->flModifications & IsModified_NetworkAdapters)
 
9539
        for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
 
9540
            if (    mNetworkAdapters[slot]
 
9541
                 && mNetworkAdapters[slot]->isModified())
 
9542
            {
 
9543
                mNetworkAdapters[slot]->rollback();
 
9544
                networkAdapters[slot] = mNetworkAdapters[slot];
 
9545
            }
 
9546
 
 
9547
    if (mData->flModifications & IsModified_SerialPorts)
 
9548
        for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
 
9549
            if (    mSerialPorts[slot]
 
9550
                 && mSerialPorts[slot]->isModified())
 
9551
            {
 
9552
                mSerialPorts[slot]->rollback();
 
9553
                serialPorts[slot] = mSerialPorts[slot];
 
9554
            }
 
9555
 
 
9556
    if (mData->flModifications & IsModified_ParallelPorts)
 
9557
        for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
 
9558
            if (    mParallelPorts[slot]
 
9559
                 && mParallelPorts[slot]->isModified())
 
9560
            {
 
9561
                mParallelPorts[slot]->rollback();
 
9562
                parallelPorts[slot] = mParallelPorts[slot];
 
9563
            }
 
9564
 
 
9565
    if (aNotify)
 
9566
    {
 
9567
        /* inform the direct session about changes */
 
9568
 
 
9569
        ComObjPtr<Machine> that = this;
 
9570
        uint32_t flModifications = mData->flModifications;
 
9571
        alock.leave();
 
9572
 
 
9573
        if (flModifications & IsModified_SharedFolders)
 
9574
            that->onSharedFolderChange();
 
9575
 
 
9576
        if (flModifications & IsModified_VRDEServer)
 
9577
            that->onVRDEServerChange(/* aRestart */ TRUE);
 
9578
        if (flModifications & IsModified_USB)
 
9579
            that->onUSBControllerChange();
 
9580
 
 
9581
        for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++)
 
9582
            if (networkAdapters[slot])
 
9583
                that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
 
9584
        for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++)
 
9585
            if (serialPorts[slot])
 
9586
                that->onSerialPortChange(serialPorts[slot]);
 
9587
        for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++)
 
9588
            if (parallelPorts[slot])
 
9589
                that->onParallelPortChange(parallelPorts[slot]);
 
9590
 
 
9591
        if (flModifications & IsModified_Storage)
 
9592
            that->onStorageControllerChange();
 
9593
 
 
9594
#if 0
 
9595
        if (flModifications & IsModified_BandwidthControl)
 
9596
            that->onBandwidthControlChange();
 
9597
#endif
 
9598
    }
 
9599
}
 
9600
 
 
9601
/**
 
9602
 * Commits all the changes to machine settings.
 
9603
 *
 
9604
 * Note that this operation is supposed to never fail.
 
9605
 *
 
9606
 * @note Locks this object and children for writing.
 
9607
 */
 
9608
void Machine::commit()
 
9609
{
 
9610
    AutoCaller autoCaller(this);
 
9611
    AssertComRCReturnVoid(autoCaller.rc());
 
9612
 
 
9613
    AutoCaller peerCaller(mPeer);
 
9614
    AssertComRCReturnVoid(peerCaller.rc());
 
9615
 
 
9616
    AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
 
9617
 
 
9618
    /*
 
9619
     *  use safe commit to ensure Snapshot machines (that share mUserData)
 
9620
     *  will still refer to a valid memory location
 
9621
     */
 
9622
    mUserData.commitCopy();
 
9623
 
 
9624
    mHWData.commit();
 
9625
 
 
9626
    if (mMediaData.isBackedUp())
 
9627
        commitMedia();
 
9628
 
 
9629
    mBIOSSettings->commit();
 
9630
    mVRDEServer->commit();
 
9631
    mAudioAdapter->commit();
 
9632
    mUSBController->commit();
 
9633
    mBandwidthControl->commit();
 
9634
 
 
9635
    for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
 
9636
        mNetworkAdapters[slot]->commit();
 
9637
    for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
 
9638
        mSerialPorts[slot]->commit();
 
9639
    for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
 
9640
        mParallelPorts[slot]->commit();
 
9641
 
 
9642
    bool commitStorageControllers = false;
 
9643
 
 
9644
    if (mStorageControllers.isBackedUp())
 
9645
    {
 
9646
        mStorageControllers.commit();
 
9647
 
 
9648
        if (mPeer)
 
9649
        {
 
9650
            AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
 
9651
 
 
9652
            /* Commit all changes to new controllers (this will reshare data with
 
9653
             * peers for those who have peers) */
 
9654
            StorageControllerList *newList = new StorageControllerList();
 
9655
            StorageControllerList::const_iterator it = mStorageControllers->begin();
 
9656
            while (it != mStorageControllers->end())
 
9657
            {
 
9658
                (*it)->commit();
 
9659
 
 
9660
                /* look if this controller has a peer device */
 
9661
                ComObjPtr<StorageController> peer = (*it)->getPeer();
 
9662
                if (!peer)
 
9663
                {
 
9664
                    /* no peer means the device is a newly created one;
 
9665
                     * create a peer owning data this device share it with */
 
9666
                    peer.createObject();
 
9667
                    peer->init(mPeer, *it, true /* aReshare */);
 
9668
                }
 
9669
                else
 
9670
                {
 
9671
                    /* remove peer from the old list */
 
9672
                    mPeer->mStorageControllers->remove(peer);
 
9673
                }
 
9674
                /* and add it to the new list */
 
9675
                newList->push_back(peer);
 
9676
 
 
9677
                ++it;
 
9678
            }
 
9679
 
 
9680
            /* uninit old peer's controllers that are left */
 
9681
            it = mPeer->mStorageControllers->begin();
 
9682
            while (it != mPeer->mStorageControllers->end())
 
9683
            {
 
9684
                (*it)->uninit();
 
9685
                ++it;
 
9686
            }
 
9687
 
 
9688
            /* attach new list of controllers to our peer */
 
9689
            mPeer->mStorageControllers.attach(newList);
 
9690
        }
 
9691
        else
 
9692
        {
 
9693
            /* we have no peer (our parent is the newly created machine);
 
9694
             * just commit changes to devices */
 
9695
            commitStorageControllers = true;
 
9696
        }
 
9697
    }
 
9698
    else
 
9699
    {
 
9700
        /* the list of controllers itself is not changed,
 
9701
         * just commit changes to controllers themselves */
 
9702
        commitStorageControllers = true;
 
9703
    }
 
9704
 
 
9705
    if (commitStorageControllers)
 
9706
    {
 
9707
        StorageControllerList::const_iterator it = mStorageControllers->begin();
 
9708
        while (it != mStorageControllers->end())
 
9709
        {
 
9710
            (*it)->commit();
 
9711
            ++it;
 
9712
        }
 
9713
    }
 
9714
 
 
9715
    if (isSessionMachine())
 
9716
    {
 
9717
        /* attach new data to the primary machine and reshare it */
 
9718
        mPeer->mUserData.attach(mUserData);
 
9719
        mPeer->mHWData.attach(mHWData);
 
9720
        /* mMediaData is reshared by fixupMedia */
 
9721
        // mPeer->mMediaData.attach(mMediaData);
 
9722
        Assert(mPeer->mMediaData.data() == mMediaData.data());
 
9723
    }
 
9724
}
 
9725
 
 
9726
/**
 
9727
 * Copies all the hardware data from the given machine.
 
9728
 *
 
9729
 * Currently, only called when the VM is being restored from a snapshot. In
 
9730
 * particular, this implies that the VM is not running during this method's
 
9731
 * call.
 
9732
 *
 
9733
 * @note This method must be called from under this object's lock.
 
9734
 *
 
9735
 * @note This method doesn't call #commit(), so all data remains backed up and
 
9736
 *       unsaved.
 
9737
 */
 
9738
void Machine::copyFrom(Machine *aThat)
 
9739
{
 
9740
    AssertReturnVoid(!isSnapshotMachine());
 
9741
    AssertReturnVoid(aThat->isSnapshotMachine());
 
9742
 
 
9743
    AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
 
9744
 
 
9745
    mHWData.assignCopy(aThat->mHWData);
 
9746
 
 
9747
    // create copies of all shared folders (mHWData after attaching a copy
 
9748
    // contains just references to original objects)
 
9749
    for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
 
9750
         it != mHWData->mSharedFolders.end();
 
9751
         ++it)
 
9752
    {
 
9753
        ComObjPtr<SharedFolder> folder;
 
9754
        folder.createObject();
 
9755
        HRESULT rc = folder->initCopy(getMachine(), *it);
 
9756
        AssertComRC(rc);
 
9757
        *it = folder;
 
9758
    }
 
9759
 
 
9760
    mBIOSSettings->copyFrom(aThat->mBIOSSettings);
 
9761
    mVRDEServer->copyFrom(aThat->mVRDEServer);
 
9762
    mAudioAdapter->copyFrom(aThat->mAudioAdapter);
 
9763
    mUSBController->copyFrom(aThat->mUSBController);
 
9764
    mBandwidthControl->copyFrom(aThat->mBandwidthControl);
 
9765
 
 
9766
    /* create private copies of all controllers */
 
9767
    mStorageControllers.backup();
 
9768
    mStorageControllers->clear();
 
9769
    for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
 
9770
         it != aThat->mStorageControllers->end();
 
9771
         ++it)
 
9772
    {
 
9773
        ComObjPtr<StorageController> ctrl;
 
9774
        ctrl.createObject();
 
9775
        ctrl->initCopy(this, *it);
 
9776
        mStorageControllers->push_back(ctrl);
 
9777
    }
 
9778
 
 
9779
    for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
 
9780
        mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
 
9781
    for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
 
9782
        mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
 
9783
    for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
 
9784
        mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
 
9785
}
 
9786
 
 
9787
#ifdef VBOX_WITH_RESOURCE_USAGE_API
 
9788
 
 
9789
void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
 
9790
{
 
9791
    AssertReturnVoid(isWriteLockOnCurrentThread());
 
9792
    AssertPtrReturnVoid(aCollector);
 
9793
 
 
9794
    pm::CollectorHAL *hal = aCollector->getHAL();
 
9795
    /* Create sub metrics */
 
9796
    pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
 
9797
        "Percentage of processor time spent in user mode by the VM process.");
 
9798
    pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
 
9799
        "Percentage of processor time spent in kernel mode by the VM process.");
 
9800
    pm::SubMetric *ramUsageUsed  = new pm::SubMetric("RAM/Usage/Used",
 
9801
        "Size of resident portion of VM process in memory.");
 
9802
    /* Create and register base metrics */
 
9803
    pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
 
9804
                                                        cpuLoadUser, cpuLoadKernel);
 
9805
    aCollector->registerBaseMetric(cpuLoad);
 
9806
    pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
 
9807
                                                       ramUsageUsed);
 
9808
    aCollector->registerBaseMetric(ramUsage);
 
9809
 
 
9810
    aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
 
9811
    aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
 
9812
                                                new pm::AggregateAvg()));
 
9813
    aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
 
9814
                                              new pm::AggregateMin()));
 
9815
    aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
 
9816
                                              new pm::AggregateMax()));
 
9817
    aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
 
9818
    aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
 
9819
                                              new pm::AggregateAvg()));
 
9820
    aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
 
9821
                                              new pm::AggregateMin()));
 
9822
    aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
 
9823
                                              new pm::AggregateMax()));
 
9824
 
 
9825
    aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
 
9826
    aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
 
9827
                                              new pm::AggregateAvg()));
 
9828
    aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
 
9829
                                              new pm::AggregateMin()));
 
9830
    aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
 
9831
                                              new pm::AggregateMax()));
 
9832
 
 
9833
 
 
9834
    /* Guest metrics */
 
9835
    mGuestHAL = new pm::CollectorGuestHAL(this, hal);
 
9836
 
 
9837
    /* Create sub metrics */
 
9838
    pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
 
9839
        "Percentage of processor time spent in user mode as seen by the guest.");
 
9840
    pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
 
9841
        "Percentage of processor time spent in kernel mode as seen by the guest.");
 
9842
    pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
 
9843
        "Percentage of processor time spent idling as seen by the guest.");
 
9844
 
 
9845
    /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
 
9846
    pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total",      "Total amount of physical guest RAM.");
 
9847
    pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free",        "Free amount of physical guest RAM.");
 
9848
    pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon",  "Amount of ballooned physical guest RAM.");
 
9849
    pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared",  "Amount of shared physical guest RAM.");
 
9850
    pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache",        "Total amount of guest (disk) cache memory.");
 
9851
 
 
9852
    pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total",    "Total amount of space in the page file.");
 
9853
 
 
9854
    /* Create and register base metrics */
 
9855
    pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mGuestHAL, aMachine, guestLoadUser, guestLoadKernel, guestLoadIdle);
 
9856
    aCollector->registerBaseMetric(guestCpuLoad);
 
9857
 
 
9858
    pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mGuestHAL, aMachine, guestMemTotal, guestMemFree, guestMemBalloon, guestMemShared,
 
9859
                                                        guestMemCache, guestPagedTotal);
 
9860
    aCollector->registerBaseMetric(guestCpuMem);
 
9861
 
 
9862
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
 
9863
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
 
9864
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
 
9865
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
 
9866
 
 
9867
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
 
9868
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
 
9869
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
 
9870
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
 
9871
 
 
9872
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
 
9873
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
 
9874
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
 
9875
    aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
 
9876
 
 
9877
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
 
9878
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
 
9879
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
 
9880
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
 
9881
 
 
9882
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
 
9883
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
 
9884
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
 
9885
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
 
9886
 
 
9887
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
 
9888
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
 
9889
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
 
9890
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
 
9891
 
 
9892
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
 
9893
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
 
9894
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
 
9895
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
 
9896
 
 
9897
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
 
9898
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
 
9899
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
 
9900
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
 
9901
 
 
9902
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
 
9903
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
 
9904
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
 
9905
    aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
 
9906
}
 
9907
 
 
9908
void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
 
9909
{
 
9910
    AssertReturnVoid(isWriteLockOnCurrentThread());
 
9911
 
 
9912
    if (aCollector)
 
9913
    {
 
9914
        aCollector->unregisterMetricsFor(aMachine);
 
9915
        aCollector->unregisterBaseMetricsFor(aMachine);
 
9916
    }
 
9917
 
 
9918
    if (mGuestHAL)
 
9919
    {
 
9920
        delete mGuestHAL;
 
9921
        mGuestHAL = NULL;
 
9922
    }
 
9923
}
 
9924
 
 
9925
#endif /* VBOX_WITH_RESOURCE_USAGE_API */
 
9926
 
 
9927
 
 
9928
////////////////////////////////////////////////////////////////////////////////
 
9929
 
 
9930
DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
 
9931
 
 
9932
HRESULT SessionMachine::FinalConstruct()
 
9933
{
 
9934
    LogFlowThisFunc(("\n"));
 
9935
 
 
9936
#if defined(RT_OS_WINDOWS)
 
9937
    mIPCSem = NULL;
 
9938
#elif defined(RT_OS_OS2)
 
9939
    mIPCSem = NULLHANDLE;
 
9940
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
9941
    mIPCSem = -1;
 
9942
#else
 
9943
# error "Port me!"
 
9944
#endif
 
9945
 
 
9946
    return S_OK;
 
9947
}
 
9948
 
 
9949
void SessionMachine::FinalRelease()
 
9950
{
 
9951
    LogFlowThisFunc(("\n"));
 
9952
 
 
9953
    uninit(Uninit::Unexpected);
 
9954
}
 
9955
 
 
9956
/**
 
9957
 *  @note Must be called only by Machine::openSession() from its own write lock.
 
9958
 */
 
9959
HRESULT SessionMachine::init(Machine *aMachine)
 
9960
{
 
9961
    LogFlowThisFuncEnter();
 
9962
    LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
 
9963
 
 
9964
    AssertReturn(aMachine, E_INVALIDARG);
 
9965
 
 
9966
    AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
 
9967
 
 
9968
    /* Enclose the state transition NotReady->InInit->Ready */
 
9969
    AutoInitSpan autoInitSpan(this);
 
9970
    AssertReturn(autoInitSpan.isOk(), E_FAIL);
 
9971
 
 
9972
    /* create the interprocess semaphore */
 
9973
#if defined(RT_OS_WINDOWS)
 
9974
    mIPCSemName = aMachine->mData->m_strConfigFileFull;
 
9975
    for (size_t i = 0; i < mIPCSemName.length(); i++)
 
9976
        if (mIPCSemName.raw()[i] == '\\')
 
9977
            mIPCSemName.raw()[i] = '/';
 
9978
    mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
 
9979
    ComAssertMsgRet(mIPCSem,
 
9980
                    ("Cannot create IPC mutex '%ls', err=%d",
 
9981
                     mIPCSemName.raw(), ::GetLastError()),
 
9982
                    E_FAIL);
 
9983
#elif defined(RT_OS_OS2)
 
9984
    Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
 
9985
                                aMachine->mData->mUuid.raw());
 
9986
    mIPCSemName = ipcSem;
 
9987
    APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
 
9988
    ComAssertMsgRet(arc == NO_ERROR,
 
9989
                    ("Cannot create IPC mutex '%s', arc=%ld",
 
9990
                     ipcSem.c_str(), arc),
 
9991
                    E_FAIL);
 
9992
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
9993
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
 
9994
#  if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
 
9995
    /** @todo Check that this still works correctly. */
 
9996
    AssertCompileSize(key_t, 8);
 
9997
#  else
 
9998
    AssertCompileSize(key_t, 4);
 
9999
#  endif
 
10000
    key_t key;
 
10001
    mIPCSem = -1;
 
10002
    mIPCKey = "0";
 
10003
    for (uint32_t i = 0; i < 1 << 24; i++)
 
10004
    {
 
10005
        key = ((uint32_t)'V' << 24) | i;
 
10006
        int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
 
10007
        if (sem >= 0 || (errno != EEXIST && errno != EACCES))
 
10008
        {
 
10009
            mIPCSem = sem;
 
10010
            if (sem >= 0)
 
10011
                mIPCKey = BstrFmt("%u", key);
 
10012
            break;
 
10013
        }
 
10014
    }
 
10015
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
 
10016
    Utf8Str semName = aMachine->mData->m_strConfigFileFull;
 
10017
    char *pszSemName = NULL;
 
10018
    RTStrUtf8ToCurrentCP(&pszSemName, semName);
 
10019
    key_t key = ::ftok(pszSemName, 'V');
 
10020
    RTStrFree(pszSemName);
 
10021
 
 
10022
    mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
 
10023
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
 
10024
 
 
10025
    int errnoSave = errno;
 
10026
    if (mIPCSem < 0 && errnoSave == ENOSYS)
 
10027
    {
 
10028
        setError(E_FAIL,
 
10029
                 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
 
10030
                    "support for SysV IPC. Check the host kernel configuration for "
 
10031
                    "CONFIG_SYSVIPC=y"));
 
10032
        return E_FAIL;
 
10033
    }
 
10034
    /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
 
10035
     * the IPC semaphores */
 
10036
    if (mIPCSem < 0 && errnoSave == ENOSPC)
 
10037
    {
 
10038
#ifdef RT_OS_LINUX
 
10039
        setError(E_FAIL,
 
10040
                 tr("Cannot create IPC semaphore because the system limit for the "
 
10041
                    "maximum number of semaphore sets (SEMMNI), or the system wide "
 
10042
                    "maximum number of semaphores (SEMMNS) would be exceeded. The "
 
10043
                    "current set of SysV IPC semaphores can be determined from "
 
10044
                    "the file /proc/sysvipc/sem"));
 
10045
#else
 
10046
        setError(E_FAIL,
 
10047
                 tr("Cannot create IPC semaphore because the system-imposed limit "
 
10048
                    "on the maximum number of allowed  semaphores or semaphore "
 
10049
                    "identifiers system-wide would be exceeded"));
 
10050
#endif
 
10051
        return E_FAIL;
 
10052
    }
 
10053
    ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
 
10054
                    E_FAIL);
 
10055
    /* set the initial value to 1 */
 
10056
    int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
 
10057
    ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
 
10058
                    E_FAIL);
 
10059
#else
 
10060
# error "Port me!"
 
10061
#endif
 
10062
 
 
10063
    /* memorize the peer Machine */
 
10064
    unconst(mPeer) = aMachine;
 
10065
    /* share the parent pointer */
 
10066
    unconst(mParent) = aMachine->mParent;
 
10067
 
 
10068
    /* take the pointers to data to share */
 
10069
    mData.share(aMachine->mData);
 
10070
    mSSData.share(aMachine->mSSData);
 
10071
 
 
10072
    mUserData.share(aMachine->mUserData);
 
10073
    mHWData.share(aMachine->mHWData);
 
10074
    mMediaData.share(aMachine->mMediaData);
 
10075
 
 
10076
    mStorageControllers.allocate();
 
10077
    for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
 
10078
         it != aMachine->mStorageControllers->end();
 
10079
         ++it)
 
10080
    {
 
10081
        ComObjPtr<StorageController> ctl;
 
10082
        ctl.createObject();
 
10083
        ctl->init(this, *it);
 
10084
        mStorageControllers->push_back(ctl);
 
10085
    }
 
10086
 
 
10087
    unconst(mBIOSSettings).createObject();
 
10088
    mBIOSSettings->init(this, aMachine->mBIOSSettings);
 
10089
    /* create another VRDEServer object that will be mutable */
 
10090
    unconst(mVRDEServer).createObject();
 
10091
    mVRDEServer->init(this, aMachine->mVRDEServer);
 
10092
    /* create another audio adapter object that will be mutable */
 
10093
    unconst(mAudioAdapter).createObject();
 
10094
    mAudioAdapter->init(this, aMachine->mAudioAdapter);
 
10095
    /* create a list of serial ports that will be mutable */
 
10096
    for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
 
10097
    {
 
10098
        unconst(mSerialPorts[slot]).createObject();
 
10099
        mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
 
10100
    }
 
10101
    /* create a list of parallel ports that will be mutable */
 
10102
    for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
 
10103
    {
 
10104
        unconst(mParallelPorts[slot]).createObject();
 
10105
        mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
 
10106
    }
 
10107
    /* create another USB controller object that will be mutable */
 
10108
    unconst(mUSBController).createObject();
 
10109
    mUSBController->init(this, aMachine->mUSBController);
 
10110
 
 
10111
    /* create a list of network adapters that will be mutable */
 
10112
    for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
 
10113
    {
 
10114
        unconst(mNetworkAdapters[slot]).createObject();
 
10115
        mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
 
10116
    }
 
10117
 
 
10118
    /* create another bandwidth control object that will be mutable */
 
10119
    unconst(mBandwidthControl).createObject();
 
10120
    mBandwidthControl->init(this, aMachine->mBandwidthControl);
 
10121
 
 
10122
    /* default is to delete saved state on Saved -> PoweredOff transition */
 
10123
    mRemoveSavedState = true;
 
10124
 
 
10125
    /* Confirm a successful initialization when it's the case */
 
10126
    autoInitSpan.setSucceeded();
 
10127
 
 
10128
    LogFlowThisFuncLeave();
 
10129
    return S_OK;
 
10130
}
 
10131
 
 
10132
/**
 
10133
 *  Uninitializes this session object. If the reason is other than
 
10134
 *  Uninit::Unexpected, then this method MUST be called from #checkForDeath().
 
10135
 *
 
10136
 *  @param aReason          uninitialization reason
 
10137
 *
 
10138
 *  @note Locks mParent + this object for writing.
 
10139
 */
 
10140
void SessionMachine::uninit(Uninit::Reason aReason)
 
10141
{
 
10142
    LogFlowThisFuncEnter();
 
10143
    LogFlowThisFunc(("reason=%d\n", aReason));
 
10144
 
 
10145
    /*
 
10146
     *  Strongly reference ourselves to prevent this object deletion after
 
10147
     *  mData->mSession.mMachine.setNull() below (which can release the last
 
10148
     *  reference and call the destructor). Important: this must be done before
 
10149
     *  accessing any members (and before AutoUninitSpan that does it as well).
 
10150
     *  This self reference will be released as the very last step on return.
 
10151
     */
 
10152
    ComObjPtr<SessionMachine> selfRef = this;
 
10153
 
 
10154
    /* Enclose the state transition Ready->InUninit->NotReady */
 
10155
    AutoUninitSpan autoUninitSpan(this);
 
10156
    if (autoUninitSpan.uninitDone())
 
10157
    {
 
10158
        LogFlowThisFunc(("Already uninitialized\n"));
 
10159
        LogFlowThisFuncLeave();
 
10160
        return;
 
10161
    }
 
10162
 
 
10163
    if (autoUninitSpan.initFailed())
 
10164
    {
 
10165
        /* We've been called by init() because it's failed. It's not really
 
10166
         * necessary (nor it's safe) to perform the regular uninit sequence
 
10167
         * below, the following is enough.
 
10168
         */
 
10169
        LogFlowThisFunc(("Initialization failed.\n"));
 
10170
#if defined(RT_OS_WINDOWS)
 
10171
        if (mIPCSem)
 
10172
            ::CloseHandle(mIPCSem);
 
10173
        mIPCSem = NULL;
 
10174
#elif defined(RT_OS_OS2)
 
10175
        if (mIPCSem != NULLHANDLE)
 
10176
            ::DosCloseMutexSem(mIPCSem);
 
10177
        mIPCSem = NULLHANDLE;
 
10178
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
10179
        if (mIPCSem >= 0)
 
10180
            ::semctl(mIPCSem, 0, IPC_RMID);
 
10181
        mIPCSem = -1;
 
10182
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
 
10183
        mIPCKey = "0";
 
10184
# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
 
10185
#else
 
10186
# error "Port me!"
 
10187
#endif
 
10188
        uninitDataAndChildObjects();
 
10189
        mData.free();
 
10190
        unconst(mParent) = NULL;
 
10191
        unconst(mPeer) = NULL;
 
10192
        LogFlowThisFuncLeave();
 
10193
        return;
 
10194
    }
 
10195
 
 
10196
    MachineState_T lastState;
 
10197
    {
 
10198
        AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
 
10199
        lastState = mData->mMachineState;
 
10200
    }
 
10201
    NOREF(lastState);
 
10202
 
 
10203
#ifdef VBOX_WITH_USB
 
10204
    // release all captured USB devices, but do this before requesting the locks below
 
10205
    if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
 
10206
    {
 
10207
        /* Console::captureUSBDevices() is called in the VM process only after
 
10208
         * setting the machine state to Starting or Restoring.
 
10209
         * Console::detachAllUSBDevices() will be called upon successful
 
10210
         * termination. So, we need to release USB devices only if there was
 
10211
         * an abnormal termination of a running VM.
 
10212
         *
 
10213
         * This is identical to SessionMachine::DetachAllUSBDevices except
 
10214
         * for the aAbnormal argument. */
 
10215
        HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
 
10216
        AssertComRC(rc);
 
10217
        NOREF(rc);
 
10218
 
 
10219
        USBProxyService *service = mParent->host()->usbProxyService();
 
10220
        if (service)
 
10221
            service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
 
10222
    }
 
10223
#endif /* VBOX_WITH_USB */
 
10224
 
 
10225
    // we need to lock this object in uninit() because the lock is shared
 
10226
    // with mPeer (as well as data we modify below). mParent->addProcessToReap()
 
10227
    // and others need mParent lock, and USB needs host lock.
 
10228
    AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
 
10229
 
 
10230
    // Trigger async cleanup tasks, avoid doing things here which are not
 
10231
    // vital to be done immediately and maybe need more locks. This calls
 
10232
    // Machine::unregisterMetrics().
 
10233
    mParent->onMachineUninit(mPeer);
 
10234
 
 
10235
    if (aReason == Uninit::Abnormal)
 
10236
    {
 
10237
        LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
 
10238
                             Global::IsOnlineOrTransient(lastState)));
 
10239
 
 
10240
        /* reset the state to Aborted */
 
10241
        if (mData->mMachineState != MachineState_Aborted)
 
10242
            setMachineState(MachineState_Aborted);
 
10243
    }
 
10244
 
 
10245
    // any machine settings modified?
 
10246
    if (mData->flModifications)
 
10247
    {
 
10248
        LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
 
10249
        rollback(false /* aNotify */);
 
10250
    }
 
10251
 
 
10252
    Assert(mConsoleTaskData.mStateFilePath.isEmpty() || !mConsoleTaskData.mSnapshot);
 
10253
    if (!mConsoleTaskData.mStateFilePath.isEmpty())
 
10254
    {
 
10255
        LogWarningThisFunc(("canceling failed save state request!\n"));
 
10256
        endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
 
10257
    }
 
10258
    else if (!mConsoleTaskData.mSnapshot.isNull())
 
10259
    {
 
10260
        LogWarningThisFunc(("canceling untaken snapshot!\n"));
 
10261
 
 
10262
        /* delete all differencing hard disks created (this will also attach
 
10263
         * their parents back by rolling back mMediaData) */
 
10264
        rollbackMedia();
 
10265
        /* delete the saved state file (it might have been already created) */
 
10266
        if (mConsoleTaskData.mSnapshot->stateFilePath().length())
 
10267
            RTFileDelete(mConsoleTaskData.mSnapshot->stateFilePath().c_str());
 
10268
 
 
10269
        mConsoleTaskData.mSnapshot->uninit();
 
10270
    }
 
10271
 
 
10272
    if (!mData->mSession.mType.isEmpty())
 
10273
    {
 
10274
        /* mType is not null when this machine's process has been started by
 
10275
         * Machine::launchVMProcess(), therefore it is our child.  We
 
10276
         * need to queue the PID to reap the process (and avoid zombies on
 
10277
         * Linux). */
 
10278
        Assert(mData->mSession.mPid != NIL_RTPROCESS);
 
10279
        mParent->addProcessToReap(mData->mSession.mPid);
 
10280
    }
 
10281
 
 
10282
    mData->mSession.mPid = NIL_RTPROCESS;
 
10283
 
 
10284
    if (aReason == Uninit::Unexpected)
 
10285
    {
 
10286
        /* Uninitialization didn't come from #checkForDeath(), so tell the
 
10287
         * client watcher thread to update the set of machines that have open
 
10288
         * sessions. */
 
10289
        mParent->updateClientWatcher();
 
10290
    }
 
10291
 
 
10292
    /* uninitialize all remote controls */
 
10293
    if (mData->mSession.mRemoteControls.size())
 
10294
    {
 
10295
        LogFlowThisFunc(("Closing remote sessions (%d):\n",
 
10296
                          mData->mSession.mRemoteControls.size()));
 
10297
 
 
10298
        Data::Session::RemoteControlList::iterator it =
 
10299
            mData->mSession.mRemoteControls.begin();
 
10300
        while (it != mData->mSession.mRemoteControls.end())
 
10301
        {
 
10302
            LogFlowThisFunc(("  Calling remoteControl->Uninitialize()...\n"));
 
10303
            HRESULT rc = (*it)->Uninitialize();
 
10304
            LogFlowThisFunc(("  remoteControl->Uninitialize() returned %08X\n", rc));
 
10305
            if (FAILED(rc))
 
10306
                LogWarningThisFunc(("Forgot to close the remote session?\n"));
 
10307
            ++it;
 
10308
        }
 
10309
        mData->mSession.mRemoteControls.clear();
 
10310
    }
 
10311
 
 
10312
    /*
 
10313
     *  An expected uninitialization can come only from #checkForDeath().
 
10314
     *  Otherwise it means that something's gone really wrong (for example,
 
10315
     *  the Session implementation has released the VirtualBox reference
 
10316
     *  before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
 
10317
     *  etc). However, it's also possible, that the client releases the IPC
 
10318
     *  semaphore correctly (i.e. before it releases the VirtualBox reference),
 
10319
     *  but the VirtualBox release event comes first to the server process.
 
10320
     *  This case is practically possible, so we should not assert on an
 
10321
     *  unexpected uninit, just log a warning.
 
10322
     */
 
10323
 
 
10324
    if ((aReason == Uninit::Unexpected))
 
10325
        LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
 
10326
 
 
10327
    if (aReason != Uninit::Normal)
 
10328
    {
 
10329
        mData->mSession.mDirectControl.setNull();
 
10330
    }
 
10331
    else
 
10332
    {
 
10333
        /* this must be null here (see #OnSessionEnd()) */
 
10334
        Assert(mData->mSession.mDirectControl.isNull());
 
10335
        Assert(mData->mSession.mState == SessionState_Unlocking);
 
10336
        Assert(!mData->mSession.mProgress.isNull());
 
10337
    }
 
10338
    if (mData->mSession.mProgress)
 
10339
    {
 
10340
        if (aReason == Uninit::Normal)
 
10341
            mData->mSession.mProgress->notifyComplete(S_OK);
 
10342
        else
 
10343
            mData->mSession.mProgress->notifyComplete(E_FAIL,
 
10344
                                                      COM_IIDOF(ISession),
 
10345
                                                      getComponentName(),
 
10346
                                                      tr("The VM session was aborted"));
 
10347
        mData->mSession.mProgress.setNull();
 
10348
    }
 
10349
 
 
10350
    /* remove the association between the peer machine and this session machine */
 
10351
    Assert(   (SessionMachine*)mData->mSession.mMachine == this
 
10352
            || aReason == Uninit::Unexpected);
 
10353
 
 
10354
    /* reset the rest of session data */
 
10355
    mData->mSession.mMachine.setNull();
 
10356
    mData->mSession.mState = SessionState_Unlocked;
 
10357
    mData->mSession.mType.setNull();
 
10358
 
 
10359
    /* close the interprocess semaphore before leaving the exclusive lock */
 
10360
#if defined(RT_OS_WINDOWS)
 
10361
    if (mIPCSem)
 
10362
        ::CloseHandle(mIPCSem);
 
10363
    mIPCSem = NULL;
 
10364
#elif defined(RT_OS_OS2)
 
10365
    if (mIPCSem != NULLHANDLE)
 
10366
        ::DosCloseMutexSem(mIPCSem);
 
10367
    mIPCSem = NULLHANDLE;
 
10368
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
10369
    if (mIPCSem >= 0)
 
10370
        ::semctl(mIPCSem, 0, IPC_RMID);
 
10371
    mIPCSem = -1;
 
10372
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
 
10373
    mIPCKey = "0";
 
10374
# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
 
10375
#else
 
10376
# error "Port me!"
 
10377
#endif
 
10378
 
 
10379
    /* fire an event */
 
10380
    mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
 
10381
 
 
10382
    uninitDataAndChildObjects();
 
10383
 
 
10384
    /* free the essential data structure last */
 
10385
    mData.free();
 
10386
 
 
10387
#if 1 /** @todo Please review this change! (bird) */
 
10388
    /* drop the exclusive lock before setting the below two to NULL */
 
10389
    multilock.release();
 
10390
#else
 
10391
    /* leave the exclusive lock before setting the below two to NULL */
 
10392
    multilock.leave();
 
10393
#endif
 
10394
 
 
10395
    unconst(mParent) = NULL;
 
10396
    unconst(mPeer) = NULL;
 
10397
 
 
10398
    LogFlowThisFuncLeave();
 
10399
}
 
10400
 
 
10401
// util::Lockable interface
 
10402
////////////////////////////////////////////////////////////////////////////////
 
10403
 
 
10404
/**
 
10405
 *  Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
 
10406
 *  with the primary Machine instance (mPeer).
 
10407
 */
 
10408
RWLockHandle *SessionMachine::lockHandle() const
 
10409
{
 
10410
    AssertReturn(mPeer != NULL, NULL);
 
10411
    return mPeer->lockHandle();
 
10412
}
 
10413
 
 
10414
// IInternalMachineControl methods
 
10415
////////////////////////////////////////////////////////////////////////////////
 
10416
 
 
10417
/**
 
10418
 *  @note Locks this object for writing.
 
10419
 */
 
10420
STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
 
10421
{
 
10422
    AutoCaller autoCaller(this);
 
10423
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10424
 
 
10425
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10426
 
 
10427
    mRemoveSavedState = aRemove;
 
10428
 
 
10429
    return S_OK;
 
10430
}
 
10431
 
 
10432
/**
 
10433
 *  @note Locks the same as #setMachineState() does.
 
10434
 */
 
10435
STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
 
10436
{
 
10437
    return setMachineState(aMachineState);
 
10438
}
 
10439
 
 
10440
/**
 
10441
 *  @note Locks this object for reading.
 
10442
 */
 
10443
STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
 
10444
{
 
10445
    AutoCaller autoCaller(this);
 
10446
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10447
 
 
10448
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10449
 
 
10450
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
 
10451
    mIPCSemName.cloneTo(aId);
 
10452
    return S_OK;
 
10453
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
10454
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
 
10455
    mIPCKey.cloneTo(aId);
 
10456
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
 
10457
    mData->m_strConfigFileFull.cloneTo(aId);
 
10458
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
 
10459
    return S_OK;
 
10460
#else
 
10461
# error "Port me!"
 
10462
#endif
 
10463
}
 
10464
 
 
10465
/**
 
10466
 *  @note Locks this object for writing.
 
10467
 */
 
10468
STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
 
10469
{
 
10470
    LogFlowThisFunc(("aProgress=%p\n", aProgress));
 
10471
    AutoCaller autoCaller(this);
 
10472
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10473
 
 
10474
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10475
 
 
10476
    if (mData->mSession.mState != SessionState_Locked)
 
10477
        return VBOX_E_INVALID_OBJECT_STATE;
 
10478
 
 
10479
    if (!mData->mSession.mProgress.isNull())
 
10480
        mData->mSession.mProgress->setOtherProgressObject(aProgress);
 
10481
 
 
10482
    LogFlowThisFunc(("returns S_OK.\n"));
 
10483
    return S_OK;
 
10484
}
 
10485
 
 
10486
/**
 
10487
 *  @note Locks this object for writing.
 
10488
 */
 
10489
STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
 
10490
{
 
10491
    AutoCaller autoCaller(this);
 
10492
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10493
 
 
10494
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10495
 
 
10496
    if (mData->mSession.mState != SessionState_Locked)
 
10497
        return VBOX_E_INVALID_OBJECT_STATE;
 
10498
 
 
10499
    /* Finalize the openRemoteSession progress object. */
 
10500
    if (mData->mSession.mProgress)
 
10501
    {
 
10502
        mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
 
10503
        mData->mSession.mProgress.setNull();
 
10504
    }
 
10505
 
 
10506
    if (SUCCEEDED((HRESULT)iResult))
 
10507
    {
 
10508
#ifdef VBOX_WITH_RESOURCE_USAGE_API
 
10509
        /* The VM has been powered up successfully, so it makes sense
 
10510
         * now to offer the performance metrics for a running machine
 
10511
         * object. Doing it earlier wouldn't be safe. */
 
10512
        registerMetrics(mParent->performanceCollector(), mPeer,
 
10513
                        mData->mSession.mPid);
 
10514
#endif /* VBOX_WITH_RESOURCE_USAGE_API */
 
10515
    }
 
10516
 
 
10517
    return S_OK;
 
10518
}
 
10519
 
 
10520
/**
 
10521
 *  @note Locks this object for writing.
 
10522
 */
 
10523
STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
 
10524
{
 
10525
    LogFlowThisFuncEnter();
 
10526
 
 
10527
    CheckComArgOutPointerValid(aProgress);
 
10528
 
 
10529
    AutoCaller autoCaller(this);
 
10530
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10531
 
 
10532
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10533
 
 
10534
    AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
 
10535
                 E_FAIL);
 
10536
 
 
10537
    /* create a progress object to track operation completion */
 
10538
    ComObjPtr<Progress> pProgress;
 
10539
    pProgress.createObject();
 
10540
    pProgress->init(getVirtualBox(),
 
10541
                    static_cast<IMachine *>(this) /* aInitiator */,
 
10542
                    Bstr(tr("Stopping the virtual machine")).raw(),
 
10543
                    FALSE /* aCancelable */);
 
10544
 
 
10545
    /* fill in the console task data */
 
10546
    mConsoleTaskData.mLastState = mData->mMachineState;
 
10547
    mConsoleTaskData.mProgress = pProgress;
 
10548
 
 
10549
    /* set the state to Stopping (this is expected by Console::PowerDown()) */
 
10550
    setMachineState(MachineState_Stopping);
 
10551
 
 
10552
    pProgress.queryInterfaceTo(aProgress);
 
10553
 
 
10554
    return S_OK;
 
10555
}
 
10556
 
 
10557
/**
 
10558
 *  @note Locks this object for writing.
 
10559
 */
 
10560
STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
 
10561
{
 
10562
    LogFlowThisFuncEnter();
 
10563
 
 
10564
    AutoCaller autoCaller(this);
 
10565
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10566
 
 
10567
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10568
 
 
10569
    AssertReturn(    (   (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
 
10570
                      || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
 
10571
                  && mConsoleTaskData.mLastState != MachineState_Null,
 
10572
                 E_FAIL);
 
10573
 
 
10574
    /*
 
10575
     * On failure, set the state to the state we had when BeginPoweringDown()
 
10576
     * was called (this is expected by Console::PowerDown() and the associated
 
10577
     * task). On success the VM process already changed the state to
 
10578
     * MachineState_PoweredOff, so no need to do anything.
 
10579
     */
 
10580
    if (FAILED(iResult))
 
10581
        setMachineState(mConsoleTaskData.mLastState);
 
10582
 
 
10583
    /* notify the progress object about operation completion */
 
10584
    Assert(mConsoleTaskData.mProgress);
 
10585
    if (SUCCEEDED(iResult))
 
10586
        mConsoleTaskData.mProgress->notifyComplete(S_OK);
 
10587
    else
 
10588
    {
 
10589
        Utf8Str strErrMsg(aErrMsg);
 
10590
        if (strErrMsg.length())
 
10591
            mConsoleTaskData.mProgress->notifyComplete(iResult,
 
10592
                                                       COM_IIDOF(ISession),
 
10593
                                                       getComponentName(),
 
10594
                                                       strErrMsg.c_str());
 
10595
        else
 
10596
            mConsoleTaskData.mProgress->notifyComplete(iResult);
 
10597
    }
 
10598
 
 
10599
    /* clear out the temporary saved state data */
 
10600
    mConsoleTaskData.mLastState = MachineState_Null;
 
10601
    mConsoleTaskData.mProgress.setNull();
 
10602
 
 
10603
    LogFlowThisFuncLeave();
 
10604
    return S_OK;
 
10605
}
 
10606
 
 
10607
 
 
10608
/**
 
10609
 *  Goes through the USB filters of the given machine to see if the given
 
10610
 *  device matches any filter or not.
 
10611
 *
 
10612
 *  @note Locks the same as USBController::hasMatchingFilter() does.
 
10613
 */
 
10614
STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
 
10615
                                                 BOOL *aMatched,
 
10616
                                                 ULONG *aMaskedIfs)
 
10617
{
 
10618
    LogFlowThisFunc(("\n"));
 
10619
 
 
10620
    CheckComArgNotNull(aUSBDevice);
 
10621
    CheckComArgOutPointerValid(aMatched);
 
10622
 
 
10623
    AutoCaller autoCaller(this);
 
10624
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10625
 
 
10626
#ifdef VBOX_WITH_USB
 
10627
    *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
 
10628
#else
 
10629
    NOREF(aUSBDevice);
 
10630
    NOREF(aMaskedIfs);
 
10631
    *aMatched = FALSE;
 
10632
#endif
 
10633
 
 
10634
    return S_OK;
 
10635
}
 
10636
 
 
10637
/**
 
10638
 *  @note Locks the same as Host::captureUSBDevice() does.
 
10639
 */
 
10640
STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
 
10641
{
 
10642
    LogFlowThisFunc(("\n"));
 
10643
 
 
10644
    AutoCaller autoCaller(this);
 
10645
    AssertComRCReturnRC(autoCaller.rc());
 
10646
 
 
10647
#ifdef VBOX_WITH_USB
 
10648
    /* if captureDeviceForVM() fails, it must have set extended error info */
 
10649
    MultiResult rc = mParent->host()->checkUSBProxyService();
 
10650
    if (FAILED(rc)) return rc;
 
10651
 
 
10652
    USBProxyService *service = mParent->host()->usbProxyService();
 
10653
    AssertReturn(service, E_FAIL);
 
10654
    return service->captureDeviceForVM(this, Guid(aId).ref());
 
10655
#else
 
10656
    NOREF(aId);
 
10657
    return E_NOTIMPL;
 
10658
#endif
 
10659
}
 
10660
 
 
10661
/**
 
10662
 *  @note Locks the same as Host::detachUSBDevice() does.
 
10663
 */
 
10664
STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
 
10665
{
 
10666
    LogFlowThisFunc(("\n"));
 
10667
 
 
10668
    AutoCaller autoCaller(this);
 
10669
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10670
 
 
10671
#ifdef VBOX_WITH_USB
 
10672
    USBProxyService *service = mParent->host()->usbProxyService();
 
10673
    AssertReturn(service, E_FAIL);
 
10674
    return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
 
10675
#else
 
10676
    NOREF(aId);
 
10677
    NOREF(aDone);
 
10678
    return E_NOTIMPL;
 
10679
#endif
 
10680
}
 
10681
 
 
10682
/**
 
10683
 *  Inserts all machine filters to the USB proxy service and then calls
 
10684
 *  Host::autoCaptureUSBDevices().
 
10685
 *
 
10686
 *  Called by Console from the VM process upon VM startup.
 
10687
 *
 
10688
 *  @note Locks what called methods lock.
 
10689
 */
 
10690
STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
 
10691
{
 
10692
    LogFlowThisFunc(("\n"));
 
10693
 
 
10694
    AutoCaller autoCaller(this);
 
10695
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10696
 
 
10697
#ifdef VBOX_WITH_USB
 
10698
    HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
 
10699
    AssertComRC(rc);
 
10700
    NOREF(rc);
 
10701
 
 
10702
    USBProxyService *service = mParent->host()->usbProxyService();
 
10703
    AssertReturn(service, E_FAIL);
 
10704
    return service->autoCaptureDevicesForVM(this);
 
10705
#else
 
10706
    return S_OK;
 
10707
#endif
 
10708
}
 
10709
 
 
10710
/**
 
10711
 *  Removes all machine filters from the USB proxy service and then calls
 
10712
 *  Host::detachAllUSBDevices().
 
10713
 *
 
10714
 *  Called by Console from the VM process upon normal VM termination or by
 
10715
 *  SessionMachine::uninit() upon abnormal VM termination (from under the
 
10716
 *  Machine/SessionMachine lock).
 
10717
 *
 
10718
 *  @note Locks what called methods lock.
 
10719
 */
 
10720
STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
 
10721
{
 
10722
    LogFlowThisFunc(("\n"));
 
10723
 
 
10724
    AutoCaller autoCaller(this);
 
10725
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10726
 
 
10727
#ifdef VBOX_WITH_USB
 
10728
    HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
 
10729
    AssertComRC(rc);
 
10730
    NOREF(rc);
 
10731
 
 
10732
    USBProxyService *service = mParent->host()->usbProxyService();
 
10733
    AssertReturn(service, E_FAIL);
 
10734
    return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
 
10735
#else
 
10736
    NOREF(aDone);
 
10737
    return S_OK;
 
10738
#endif
 
10739
}
 
10740
 
 
10741
/**
 
10742
 *  @note Locks this object for writing.
 
10743
 */
 
10744
STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
 
10745
                                          IProgress **aProgress)
 
10746
{
 
10747
    LogFlowThisFuncEnter();
 
10748
 
 
10749
    AssertReturn(aSession, E_INVALIDARG);
 
10750
    AssertReturn(aProgress, E_INVALIDARG);
 
10751
 
 
10752
    AutoCaller autoCaller(this);
 
10753
 
 
10754
    LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
 
10755
    /*
 
10756
     *  We don't assert below because it might happen that a non-direct session
 
10757
     *  informs us it is closed right after we've been uninitialized -- it's ok.
 
10758
     */
 
10759
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
10760
 
 
10761
    /* get IInternalSessionControl interface */
 
10762
    ComPtr<IInternalSessionControl> control(aSession);
 
10763
 
 
10764
    ComAssertRet(!control.isNull(), E_INVALIDARG);
 
10765
 
 
10766
    /* Creating a Progress object requires the VirtualBox lock, and
 
10767
     * thus locking it here is required by the lock order rules. */
 
10768
    AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
 
10769
 
 
10770
    if (control == mData->mSession.mDirectControl)
 
10771
    {
 
10772
        ComAssertRet(aProgress, E_POINTER);
 
10773
 
 
10774
        /* The direct session is being normally closed by the client process
 
10775
         * ----------------------------------------------------------------- */
 
10776
 
 
10777
        /* go to the closing state (essential for all open*Session() calls and
 
10778
         * for #checkForDeath()) */
 
10779
        Assert(mData->mSession.mState == SessionState_Locked);
 
10780
        mData->mSession.mState = SessionState_Unlocking;
 
10781
 
 
10782
        /* set direct control to NULL to release the remote instance */
 
10783
        mData->mSession.mDirectControl.setNull();
 
10784
        LogFlowThisFunc(("Direct control is set to NULL\n"));
 
10785
 
 
10786
        if (mData->mSession.mProgress)
 
10787
        {
 
10788
            /* finalize the progress, someone might wait if a frontend
 
10789
             * closes the session before powering on the VM. */
 
10790
            mData->mSession.mProgress->notifyComplete(E_FAIL,
 
10791
                                                      COM_IIDOF(ISession),
 
10792
                                                      getComponentName(),
 
10793
                                                      tr("The VM session was closed before any attempt to power it on"));
 
10794
            mData->mSession.mProgress.setNull();
 
10795
        }
 
10796
 
 
10797
        /*  Create the progress object the client will use to wait until
 
10798
         * #checkForDeath() is called to uninitialize this session object after
 
10799
         * it releases the IPC semaphore.
 
10800
         * Note! Because we're "reusing" mProgress here, this must be a proxy
 
10801
         *       object just like for openRemoteSession. */
 
10802
        Assert(mData->mSession.mProgress.isNull());
 
10803
        ComObjPtr<ProgressProxy> progress;
 
10804
        progress.createObject();
 
10805
        ComPtr<IUnknown> pPeer(mPeer);
 
10806
        progress->init(mParent, pPeer,
 
10807
                       Bstr(tr("Closing session")).raw(),
 
10808
                       FALSE /* aCancelable */);
 
10809
        progress.queryInterfaceTo(aProgress);
 
10810
        mData->mSession.mProgress = progress;
 
10811
    }
 
10812
    else
 
10813
    {
 
10814
        /* the remote session is being normally closed */
 
10815
        Data::Session::RemoteControlList::iterator it =
 
10816
            mData->mSession.mRemoteControls.begin();
 
10817
        while (it != mData->mSession.mRemoteControls.end())
 
10818
        {
 
10819
            if (control == *it)
 
10820
                break;
 
10821
            ++it;
 
10822
        }
 
10823
        BOOL found = it != mData->mSession.mRemoteControls.end();
 
10824
        ComAssertMsgRet(found, ("The session is not found in the session list!"),
 
10825
                         E_INVALIDARG);
 
10826
        mData->mSession.mRemoteControls.remove(*it);
 
10827
    }
 
10828
 
 
10829
    LogFlowThisFuncLeave();
 
10830
    return S_OK;
 
10831
}
 
10832
 
 
10833
/**
 
10834
 *  @note Locks this object for writing.
 
10835
 */
 
10836
STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
 
10837
{
 
10838
    LogFlowThisFuncEnter();
 
10839
 
 
10840
    CheckComArgOutPointerValid(aProgress);
 
10841
    CheckComArgOutPointerValid(aStateFilePath);
 
10842
 
 
10843
    AutoCaller autoCaller(this);
 
10844
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10845
 
 
10846
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10847
 
 
10848
    AssertReturn(    mData->mMachineState == MachineState_Paused
 
10849
                  && mConsoleTaskData.mLastState == MachineState_Null
 
10850
                  && mConsoleTaskData.mStateFilePath.isEmpty(),
 
10851
                 E_FAIL);
 
10852
 
 
10853
    /* create a progress object to track operation completion */
 
10854
    ComObjPtr<Progress> pProgress;
 
10855
    pProgress.createObject();
 
10856
    pProgress->init(getVirtualBox(),
 
10857
                    static_cast<IMachine *>(this) /* aInitiator */,
 
10858
                    Bstr(tr("Saving the execution state of the virtual machine")).raw(),
 
10859
                    FALSE /* aCancelable */);
 
10860
 
 
10861
    Bstr stateFilePath;
 
10862
    /* stateFilePath is null when the machine is not running */
 
10863
    if (mData->mMachineState == MachineState_Paused)
 
10864
    {
 
10865
        Utf8Str strFullSnapshotFolder;
 
10866
        calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
 
10867
        stateFilePath = Utf8StrFmt("%s%c{%RTuuid}.sav",
 
10868
                                   strFullSnapshotFolder.c_str(),
 
10869
                                   RTPATH_DELIMITER,
 
10870
                                   mData->mUuid.raw());
 
10871
    }
 
10872
 
 
10873
    /* fill in the console task data */
 
10874
    mConsoleTaskData.mLastState = mData->mMachineState;
 
10875
    mConsoleTaskData.mStateFilePath = stateFilePath;
 
10876
    mConsoleTaskData.mProgress = pProgress;
 
10877
 
 
10878
    /* set the state to Saving (this is expected by Console::SaveState()) */
 
10879
    setMachineState(MachineState_Saving);
 
10880
 
 
10881
    stateFilePath.cloneTo(aStateFilePath);
 
10882
    pProgress.queryInterfaceTo(aProgress);
 
10883
 
 
10884
    return S_OK;
 
10885
}
 
10886
 
 
10887
/**
 
10888
 *  @note Locks mParent + this object for writing.
 
10889
 */
 
10890
STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
 
10891
{
 
10892
    LogFlowThisFunc(("\n"));
 
10893
 
 
10894
    AutoCaller autoCaller(this);
 
10895
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10896
 
 
10897
    /* endSavingState() need mParent lock */
 
10898
    AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
 
10899
 
 
10900
    AssertReturn(    (   (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
 
10901
                      || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
 
10902
                  && mConsoleTaskData.mLastState != MachineState_Null
 
10903
                  && !mConsoleTaskData.mStateFilePath.isEmpty(),
 
10904
                 E_FAIL);
 
10905
 
 
10906
    /*
 
10907
     * On failure, set the state to the state we had when BeginSavingState()
 
10908
     * was called (this is expected by Console::SaveState() and the associated
 
10909
     * task). On success the VM process already changed the state to
 
10910
     * MachineState_Saved, so no need to do anything.
 
10911
     */
 
10912
    if (FAILED(iResult))
 
10913
        setMachineState(mConsoleTaskData.mLastState);
 
10914
 
 
10915
    return endSavingState(iResult, aErrMsg);
 
10916
}
 
10917
 
 
10918
/**
 
10919
 *  @note Locks this object for writing.
 
10920
 */
 
10921
STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
 
10922
{
 
10923
    LogFlowThisFunc(("\n"));
 
10924
 
 
10925
    CheckComArgStrNotEmptyOrNull(aSavedStateFile);
 
10926
 
 
10927
    AutoCaller autoCaller(this);
 
10928
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10929
 
 
10930
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10931
 
 
10932
    AssertReturn(   mData->mMachineState == MachineState_PoweredOff
 
10933
                 || mData->mMachineState == MachineState_Teleported
 
10934
                 || mData->mMachineState == MachineState_Aborted
 
10935
                 , E_FAIL); /** @todo setError. */
 
10936
 
 
10937
    Utf8Str stateFilePathFull = aSavedStateFile;
 
10938
    int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
 
10939
    if (RT_FAILURE(vrc))
 
10940
        return setError(VBOX_E_FILE_ERROR,
 
10941
                        tr("Invalid saved state file path '%ls' (%Rrc)"),
 
10942
                        aSavedStateFile,
 
10943
                        vrc);
 
10944
 
 
10945
    mSSData->mStateFilePath = stateFilePathFull;
 
10946
 
 
10947
    /* The below setMachineState() will detect the state transition and will
 
10948
     * update the settings file */
 
10949
 
 
10950
    return setMachineState(MachineState_Saved);
 
10951
}
 
10952
 
 
10953
STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
 
10954
                                                 ComSafeArrayOut(BSTR, aValues),
 
10955
                                                 ComSafeArrayOut(LONG64, aTimestamps),
 
10956
                                                 ComSafeArrayOut(BSTR, aFlags))
 
10957
{
 
10958
    LogFlowThisFunc(("\n"));
 
10959
 
 
10960
#ifdef VBOX_WITH_GUEST_PROPS
 
10961
    using namespace guestProp;
 
10962
 
 
10963
    AutoCaller autoCaller(this);
 
10964
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
10965
 
 
10966
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
10967
 
 
10968
    AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
 
10969
    AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
 
10970
    AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
 
10971
    AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
 
10972
 
 
10973
    size_t cEntries = mHWData->mGuestProperties.size();
 
10974
    com::SafeArray<BSTR> names(cEntries);
 
10975
    com::SafeArray<BSTR> values(cEntries);
 
10976
    com::SafeArray<LONG64> timestamps(cEntries);
 
10977
    com::SafeArray<BSTR> flags(cEntries);
 
10978
    unsigned i = 0;
 
10979
    for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
 
10980
         it != mHWData->mGuestProperties.end();
 
10981
         ++it)
 
10982
    {
 
10983
        char szFlags[MAX_FLAGS_LEN + 1];
 
10984
        it->strName.cloneTo(&names[i]);
 
10985
        it->strValue.cloneTo(&values[i]);
 
10986
        timestamps[i] = it->mTimestamp;
 
10987
        /* If it is NULL, keep it NULL. */
 
10988
        if (it->mFlags)
 
10989
        {
 
10990
            writeFlags(it->mFlags, szFlags);
 
10991
            Bstr(szFlags).cloneTo(&flags[i]);
 
10992
        }
 
10993
        else
 
10994
            flags[i] = NULL;
 
10995
        ++i;
 
10996
    }
 
10997
    names.detachTo(ComSafeArrayOutArg(aNames));
 
10998
    values.detachTo(ComSafeArrayOutArg(aValues));
 
10999
    timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
 
11000
    flags.detachTo(ComSafeArrayOutArg(aFlags));
 
11001
    return S_OK;
 
11002
#else
 
11003
    ReturnComNotImplemented();
 
11004
#endif
 
11005
}
 
11006
 
 
11007
STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
 
11008
                                               IN_BSTR aValue,
 
11009
                                               LONG64 aTimestamp,
 
11010
                                               IN_BSTR aFlags)
 
11011
{
 
11012
    LogFlowThisFunc(("\n"));
 
11013
 
 
11014
#ifdef VBOX_WITH_GUEST_PROPS
 
11015
    using namespace guestProp;
 
11016
 
 
11017
    CheckComArgStrNotEmptyOrNull(aName);
 
11018
    if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
 
11019
        return E_POINTER;  /* aValue can be NULL to indicate deletion */
 
11020
 
 
11021
    try
 
11022
    {
 
11023
        /*
 
11024
         * Convert input up front.
 
11025
         */
 
11026
        Utf8Str     utf8Name(aName);
 
11027
        uint32_t    fFlags = NILFLAG;
 
11028
        if (aFlags)
 
11029
        {
 
11030
            Utf8Str utf8Flags(aFlags);
 
11031
            int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
 
11032
            AssertRCReturn(vrc, E_INVALIDARG);
 
11033
        }
 
11034
 
 
11035
        /*
 
11036
         * Now grab the object lock, validate the state and do the update.
 
11037
         */
 
11038
        AutoCaller autoCaller(this);
 
11039
        if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
11040
 
 
11041
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11042
 
 
11043
        switch (mData->mMachineState)
 
11044
        {
 
11045
            case MachineState_Paused:
 
11046
            case MachineState_Running:
 
11047
            case MachineState_Teleporting:
 
11048
            case MachineState_TeleportingPausedVM:
 
11049
            case MachineState_LiveSnapshotting:
 
11050
            case MachineState_DeletingSnapshotOnline:
 
11051
            case MachineState_DeletingSnapshotPaused:
 
11052
            case MachineState_Saving:
 
11053
                break;
 
11054
 
 
11055
            default:
 
11056
#ifndef DEBUG_sunlover
 
11057
                AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
 
11058
                                      VBOX_E_INVALID_VM_STATE);
 
11059
#else
 
11060
                return VBOX_E_INVALID_VM_STATE;
 
11061
#endif
 
11062
        }
 
11063
 
 
11064
        setModified(IsModified_MachineData);
 
11065
        mHWData.backup();
 
11066
 
 
11067
        /** @todo r=bird: The careful memory handling doesn't work out here because
 
11068
         *  the catch block won't undo any damage we've done.  So, if push_back throws
 
11069
         *  bad_alloc then you've lost the value.
 
11070
         *
 
11071
         *  Another thing. Doing a linear search here isn't extremely efficient, esp.
 
11072
         *  since values that changes actually bubbles to the end of the list.  Using
 
11073
         *  something that has an efficient lookup and can tolerate a bit of updates
 
11074
         *  would be nice.  RTStrSpace is one suggestion (it's not perfect).  Some
 
11075
         *  combination of RTStrCache (for sharing names and getting uniqueness into
 
11076
         *  the bargain) and hash/tree is another. */
 
11077
        for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
 
11078
             iter != mHWData->mGuestProperties.end();
 
11079
             ++iter)
 
11080
            if (utf8Name == iter->strName)
 
11081
            {
 
11082
                mHWData->mGuestProperties.erase(iter);
 
11083
                mData->mGuestPropertiesModified = TRUE;
 
11084
                break;
 
11085
            }
 
11086
        if (aValue != NULL)
 
11087
        {
 
11088
            HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
 
11089
            mHWData->mGuestProperties.push_back(property);
 
11090
            mData->mGuestPropertiesModified = TRUE;
 
11091
        }
 
11092
 
 
11093
        /*
 
11094
         * Send a callback notification if appropriate
 
11095
         */
 
11096
        if (    mHWData->mGuestPropertyNotificationPatterns.isEmpty()
 
11097
             || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
 
11098
                                             RTSTR_MAX,
 
11099
                                             utf8Name.c_str(),
 
11100
                                             RTSTR_MAX, NULL)
 
11101
           )
 
11102
        {
 
11103
            alock.leave();
 
11104
 
 
11105
            mParent->onGuestPropertyChange(mData->mUuid,
 
11106
                                           aName,
 
11107
                                           aValue,
 
11108
                                           aFlags);
 
11109
        }
 
11110
    }
 
11111
    catch (...)
 
11112
    {
 
11113
        return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
 
11114
    }
 
11115
    return S_OK;
 
11116
#else
 
11117
    ReturnComNotImplemented();
 
11118
#endif
 
11119
}
 
11120
 
 
11121
// public methods only for internal purposes
 
11122
/////////////////////////////////////////////////////////////////////////////
 
11123
 
 
11124
/**
 
11125
 * Called from the client watcher thread to check for expected or unexpected
 
11126
 * death of the client process that has a direct session to this machine.
 
11127
 *
 
11128
 * On Win32 and on OS/2, this method is called only when we've got the
 
11129
 * mutex (i.e. the client has either died or terminated normally) so it always
 
11130
 * returns @c true (the client is terminated, the session machine is
 
11131
 * uninitialized).
 
11132
 *
 
11133
 * On other platforms, the method returns @c true if the client process has
 
11134
 * terminated normally or abnormally and the session machine was uninitialized,
 
11135
 * and @c false if the client process is still alive.
 
11136
 *
 
11137
 * @note Locks this object for writing.
 
11138
 */
 
11139
bool SessionMachine::checkForDeath()
 
11140
{
 
11141
    Uninit::Reason reason;
 
11142
    bool terminated = false;
 
11143
 
 
11144
    /* Enclose autoCaller with a block because calling uninit() from under it
 
11145
     * will deadlock. */
 
11146
    {
 
11147
        AutoCaller autoCaller(this);
 
11148
        if (!autoCaller.isOk())
 
11149
        {
 
11150
            /* return true if not ready, to cause the client watcher to exclude
 
11151
             * the corresponding session from watching */
 
11152
            LogFlowThisFunc(("Already uninitialized!\n"));
 
11153
            return true;
 
11154
        }
 
11155
 
 
11156
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11157
 
 
11158
        /* Determine the reason of death: if the session state is Closing here,
 
11159
         * everything is fine. Otherwise it means that the client did not call
 
11160
         * OnSessionEnd() before it released the IPC semaphore. This may happen
 
11161
         * either because the client process has abnormally terminated, or
 
11162
         * because it simply forgot to call ISession::Close() before exiting. We
 
11163
         * threat the latter also as an abnormal termination (see
 
11164
         * Session::uninit() for details). */
 
11165
        reason = mData->mSession.mState == SessionState_Unlocking ?
 
11166
                 Uninit::Normal :
 
11167
                 Uninit::Abnormal;
 
11168
 
 
11169
#if defined(RT_OS_WINDOWS)
 
11170
 
 
11171
        AssertMsg(mIPCSem, ("semaphore must be created"));
 
11172
 
 
11173
        /* release the IPC mutex */
 
11174
        ::ReleaseMutex(mIPCSem);
 
11175
 
 
11176
        terminated = true;
 
11177
 
 
11178
#elif defined(RT_OS_OS2)
 
11179
 
 
11180
        AssertMsg(mIPCSem, ("semaphore must be created"));
 
11181
 
 
11182
        /* release the IPC mutex */
 
11183
        ::DosReleaseMutexSem(mIPCSem);
 
11184
 
 
11185
        terminated = true;
 
11186
 
 
11187
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
11188
 
 
11189
        AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
 
11190
 
 
11191
        int val = ::semctl(mIPCSem, 0, GETVAL);
 
11192
        if (val > 0)
 
11193
        {
 
11194
            /* the semaphore is signaled, meaning the session is terminated */
 
11195
            terminated = true;
 
11196
        }
 
11197
 
 
11198
#else
 
11199
# error "Port me!"
 
11200
#endif
 
11201
 
 
11202
    } /* AutoCaller block */
 
11203
 
 
11204
    if (terminated)
 
11205
        uninit(reason);
 
11206
 
 
11207
    return terminated;
 
11208
}
 
11209
 
 
11210
/**
 
11211
 *  @note Locks this object for reading.
 
11212
 */
 
11213
HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
 
11214
{
 
11215
    LogFlowThisFunc(("\n"));
 
11216
 
 
11217
    AutoCaller autoCaller(this);
 
11218
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11219
 
 
11220
    ComPtr<IInternalSessionControl> directControl;
 
11221
    {
 
11222
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11223
        directControl = mData->mSession.mDirectControl;
 
11224
    }
 
11225
 
 
11226
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11227
    if (!directControl)
 
11228
        return S_OK;
 
11229
 
 
11230
    return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
 
11231
}
 
11232
 
 
11233
/**
 
11234
 *  @note Locks this object for reading.
 
11235
 */
 
11236
HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
 
11237
                                 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
 
11238
{
 
11239
    LogFlowThisFunc(("\n"));
 
11240
 
 
11241
    AutoCaller autoCaller(this);
 
11242
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11243
 
 
11244
    ComPtr<IInternalSessionControl> directControl;
 
11245
    {
 
11246
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11247
        directControl = mData->mSession.mDirectControl;
 
11248
    }
 
11249
 
 
11250
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11251
    if (!directControl)
 
11252
        return S_OK;
 
11253
    /*
 
11254
     * instead acting like callback we ask IVirtualBox deliver corresponding event
 
11255
     */
 
11256
 
 
11257
    mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, aHostPort, aGuestIp, aGuestPort);
 
11258
    return S_OK;
 
11259
}
 
11260
 
 
11261
/**
 
11262
 *  @note Locks this object for reading.
 
11263
 */
 
11264
HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
 
11265
{
 
11266
    LogFlowThisFunc(("\n"));
 
11267
 
 
11268
    AutoCaller autoCaller(this);
 
11269
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11270
 
 
11271
    ComPtr<IInternalSessionControl> directControl;
 
11272
    {
 
11273
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11274
        directControl = mData->mSession.mDirectControl;
 
11275
    }
 
11276
 
 
11277
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11278
    if (!directControl)
 
11279
        return S_OK;
 
11280
 
 
11281
    return directControl->OnSerialPortChange(serialPort);
 
11282
}
 
11283
 
 
11284
/**
 
11285
 *  @note Locks this object for reading.
 
11286
 */
 
11287
HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
 
11288
{
 
11289
    LogFlowThisFunc(("\n"));
 
11290
 
 
11291
    AutoCaller autoCaller(this);
 
11292
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11293
 
 
11294
    ComPtr<IInternalSessionControl> directControl;
 
11295
    {
 
11296
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11297
        directControl = mData->mSession.mDirectControl;
 
11298
    }
 
11299
 
 
11300
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11301
    if (!directControl)
 
11302
        return S_OK;
 
11303
 
 
11304
    return directControl->OnParallelPortChange(parallelPort);
 
11305
}
 
11306
 
 
11307
/**
 
11308
 *  @note Locks this object for reading.
 
11309
 */
 
11310
HRESULT SessionMachine::onStorageControllerChange()
 
11311
{
 
11312
    LogFlowThisFunc(("\n"));
 
11313
 
 
11314
    AutoCaller autoCaller(this);
 
11315
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11316
 
 
11317
    ComPtr<IInternalSessionControl> directControl;
 
11318
    {
 
11319
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11320
        directControl = mData->mSession.mDirectControl;
 
11321
    }
 
11322
 
 
11323
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11324
    if (!directControl)
 
11325
        return S_OK;
 
11326
 
 
11327
    return directControl->OnStorageControllerChange();
 
11328
}
 
11329
 
 
11330
/**
 
11331
 *  @note Locks this object for reading.
 
11332
 */
 
11333
HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
 
11334
{
 
11335
    LogFlowThisFunc(("\n"));
 
11336
 
 
11337
    AutoCaller autoCaller(this);
 
11338
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11339
 
 
11340
    ComPtr<IInternalSessionControl> directControl;
 
11341
    {
 
11342
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11343
        directControl = mData->mSession.mDirectControl;
 
11344
    }
 
11345
 
 
11346
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11347
    if (!directControl)
 
11348
        return S_OK;
 
11349
 
 
11350
    return directControl->OnMediumChange(aAttachment, aForce);
 
11351
}
 
11352
 
 
11353
/**
 
11354
 *  @note Locks this object for reading.
 
11355
 */
 
11356
HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
 
11357
{
 
11358
    LogFlowThisFunc(("\n"));
 
11359
 
 
11360
    AutoCaller autoCaller(this);
 
11361
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
11362
 
 
11363
    ComPtr<IInternalSessionControl> directControl;
 
11364
    {
 
11365
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11366
        directControl = mData->mSession.mDirectControl;
 
11367
    }
 
11368
 
 
11369
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11370
    if (!directControl)
 
11371
        return S_OK;
 
11372
 
 
11373
    return directControl->OnCPUChange(aCPU, aRemove);
 
11374
}
 
11375
 
 
11376
HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
 
11377
{
 
11378
    LogFlowThisFunc(("\n"));
 
11379
 
 
11380
    AutoCaller autoCaller(this);
 
11381
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
11382
 
 
11383
    ComPtr<IInternalSessionControl> directControl;
 
11384
    {
 
11385
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11386
        directControl = mData->mSession.mDirectControl;
 
11387
    }
 
11388
 
 
11389
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11390
    if (!directControl)
 
11391
        return S_OK;
 
11392
 
 
11393
    return directControl->OnCPUExecutionCapChange(aExecutionCap);
 
11394
}
 
11395
 
 
11396
/**
 
11397
 *  @note Locks this object for reading.
 
11398
 */
 
11399
HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
 
11400
{
 
11401
    LogFlowThisFunc(("\n"));
 
11402
 
 
11403
    AutoCaller autoCaller(this);
 
11404
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11405
 
 
11406
    ComPtr<IInternalSessionControl> directControl;
 
11407
    {
 
11408
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11409
        directControl = mData->mSession.mDirectControl;
 
11410
    }
 
11411
 
 
11412
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11413
    if (!directControl)
 
11414
        return S_OK;
 
11415
 
 
11416
    return directControl->OnVRDEServerChange(aRestart);
 
11417
}
 
11418
 
 
11419
/**
 
11420
 *  @note Locks this object for reading.
 
11421
 */
 
11422
HRESULT SessionMachine::onUSBControllerChange()
 
11423
{
 
11424
    LogFlowThisFunc(("\n"));
 
11425
 
 
11426
    AutoCaller autoCaller(this);
 
11427
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11428
 
 
11429
    ComPtr<IInternalSessionControl> directControl;
 
11430
    {
 
11431
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11432
        directControl = mData->mSession.mDirectControl;
 
11433
    }
 
11434
 
 
11435
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11436
    if (!directControl)
 
11437
        return S_OK;
 
11438
 
 
11439
    return directControl->OnUSBControllerChange();
 
11440
}
 
11441
 
 
11442
/**
 
11443
 *  @note Locks this object for reading.
 
11444
 */
 
11445
HRESULT SessionMachine::onSharedFolderChange()
 
11446
{
 
11447
    LogFlowThisFunc(("\n"));
 
11448
 
 
11449
    AutoCaller autoCaller(this);
 
11450
    AssertComRCReturnRC(autoCaller.rc());
 
11451
 
 
11452
    ComPtr<IInternalSessionControl> directControl;
 
11453
    {
 
11454
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11455
        directControl = mData->mSession.mDirectControl;
 
11456
    }
 
11457
 
 
11458
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11459
    if (!directControl)
 
11460
        return S_OK;
 
11461
 
 
11462
    return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
 
11463
}
 
11464
 
 
11465
/**
 
11466
 *  @note Locks this object for reading.
 
11467
 */
 
11468
HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
 
11469
{
 
11470
    LogFlowThisFunc(("\n"));
 
11471
 
 
11472
    AutoCaller autoCaller(this);
 
11473
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
11474
 
 
11475
    ComPtr<IInternalSessionControl> directControl;
 
11476
    {
 
11477
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11478
        directControl = mData->mSession.mDirectControl;
 
11479
    }
 
11480
 
 
11481
    /* ignore notifications sent after #OnSessionEnd() is called */
 
11482
    if (!directControl)
 
11483
        return S_OK;
 
11484
 
 
11485
    return directControl->OnBandwidthGroupChange(aBandwidthGroup);
 
11486
}
 
11487
 
 
11488
/**
 
11489
 *  Returns @c true if this machine's USB controller reports it has a matching
 
11490
 *  filter for the given USB device and @c false otherwise.
 
11491
 *
 
11492
 *  @note Caller must have requested machine read lock.
 
11493
 */
 
11494
bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
 
11495
{
 
11496
    AutoCaller autoCaller(this);
 
11497
    /* silently return if not ready -- this method may be called after the
 
11498
     * direct machine session has been called */
 
11499
    if (!autoCaller.isOk())
 
11500
        return false;
 
11501
 
 
11502
 
 
11503
#ifdef VBOX_WITH_USB
 
11504
    switch (mData->mMachineState)
 
11505
    {
 
11506
        case MachineState_Starting:
 
11507
        case MachineState_Restoring:
 
11508
        case MachineState_TeleportingIn:
 
11509
        case MachineState_Paused:
 
11510
        case MachineState_Running:
 
11511
        /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
 
11512
         *        elsewhere... */
 
11513
            return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
 
11514
        default: break;
 
11515
    }
 
11516
#else
 
11517
    NOREF(aDevice);
 
11518
    NOREF(aMaskedIfs);
 
11519
#endif
 
11520
    return false;
 
11521
}
 
11522
 
 
11523
/**
 
11524
 *  @note The calls shall hold no locks. Will temporarily lock this object for reading.
 
11525
 */
 
11526
HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
 
11527
                                          IVirtualBoxErrorInfo *aError,
 
11528
                                          ULONG aMaskedIfs)
 
11529
{
 
11530
    LogFlowThisFunc(("\n"));
 
11531
 
 
11532
    AutoCaller autoCaller(this);
 
11533
 
 
11534
    /* This notification may happen after the machine object has been
 
11535
     * uninitialized (the session was closed), so don't assert. */
 
11536
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
11537
 
 
11538
    ComPtr<IInternalSessionControl> directControl;
 
11539
    {
 
11540
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11541
        directControl = mData->mSession.mDirectControl;
 
11542
    }
 
11543
 
 
11544
    /* fail on notifications sent after #OnSessionEnd() is called, it is
 
11545
     * expected by the caller */
 
11546
    if (!directControl)
 
11547
        return E_FAIL;
 
11548
 
 
11549
    /* No locks should be held at this point. */
 
11550
    AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
 
11551
    AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
 
11552
 
 
11553
    return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
 
11554
}
 
11555
 
 
11556
/**
 
11557
 *  @note The calls shall hold no locks. Will temporarily lock this object for reading.
 
11558
 */
 
11559
HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
 
11560
                                          IVirtualBoxErrorInfo *aError)
 
11561
{
 
11562
    LogFlowThisFunc(("\n"));
 
11563
 
 
11564
    AutoCaller autoCaller(this);
 
11565
 
 
11566
    /* This notification may happen after the machine object has been
 
11567
     * uninitialized (the session was closed), so don't assert. */
 
11568
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
11569
 
 
11570
    ComPtr<IInternalSessionControl> directControl;
 
11571
    {
 
11572
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11573
        directControl = mData->mSession.mDirectControl;
 
11574
    }
 
11575
 
 
11576
    /* fail on notifications sent after #OnSessionEnd() is called, it is
 
11577
     * expected by the caller */
 
11578
    if (!directControl)
 
11579
        return E_FAIL;
 
11580
 
 
11581
    /* No locks should be held at this point. */
 
11582
    AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
 
11583
    AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
 
11584
 
 
11585
    return directControl->OnUSBDeviceDetach(aId, aError);
 
11586
}
 
11587
 
 
11588
// protected methods
 
11589
/////////////////////////////////////////////////////////////////////////////
 
11590
 
 
11591
/**
 
11592
 *  Helper method to finalize saving the state.
 
11593
 *
 
11594
 *  @note Must be called from under this object's lock.
 
11595
 *
 
11596
 *  @param aRc      S_OK if the snapshot has been taken successfully
 
11597
 *  @param aErrMsg  human readable error message for failure
 
11598
 *
 
11599
 *  @note Locks mParent + this objects for writing.
 
11600
 */
 
11601
HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
 
11602
{
 
11603
    LogFlowThisFuncEnter();
 
11604
 
 
11605
    AutoCaller autoCaller(this);
 
11606
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11607
 
 
11608
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11609
 
 
11610
    HRESULT rc = S_OK;
 
11611
 
 
11612
    if (SUCCEEDED(aRc))
 
11613
    {
 
11614
        mSSData->mStateFilePath = mConsoleTaskData.mStateFilePath;
 
11615
 
 
11616
        /* save all VM settings */
 
11617
        rc = saveSettings(NULL);
 
11618
                // no need to check whether VirtualBox.xml needs saving also since
 
11619
                // we can't have a name change pending at this point
 
11620
    }
 
11621
    else
 
11622
    {
 
11623
        /* delete the saved state file (it might have been already created) */
 
11624
        RTFileDelete(mConsoleTaskData.mStateFilePath.c_str());
 
11625
    }
 
11626
 
 
11627
    /* notify the progress object about operation completion */
 
11628
    Assert(mConsoleTaskData.mProgress);
 
11629
    if (SUCCEEDED(aRc))
 
11630
        mConsoleTaskData.mProgress->notifyComplete(S_OK);
 
11631
    else
 
11632
    {
 
11633
        if (aErrMsg.length())
 
11634
            mConsoleTaskData.mProgress->notifyComplete(aRc,
 
11635
                                                       COM_IIDOF(ISession),
 
11636
                                                       getComponentName(),
 
11637
                                                       aErrMsg.c_str());
 
11638
        else
 
11639
            mConsoleTaskData.mProgress->notifyComplete(aRc);
 
11640
    }
 
11641
 
 
11642
    /* clear out the temporary saved state data */
 
11643
    mConsoleTaskData.mLastState = MachineState_Null;
 
11644
    mConsoleTaskData.mStateFilePath.setNull();
 
11645
    mConsoleTaskData.mProgress.setNull();
 
11646
 
 
11647
    LogFlowThisFuncLeave();
 
11648
    return rc;
 
11649
}
 
11650
 
 
11651
/**
 
11652
 * Locks the attached media.
 
11653
 *
 
11654
 * All attached hard disks are locked for writing and DVD/floppy are locked for
 
11655
 * reading. Parents of attached hard disks (if any) are locked for reading.
 
11656
 *
 
11657
 * This method also performs accessibility check of all media it locks: if some
 
11658
 * media is inaccessible, the method will return a failure and a bunch of
 
11659
 * extended error info objects per each inaccessible medium.
 
11660
 *
 
11661
 * Note that this method is atomic: if it returns a success, all media are
 
11662
 * locked as described above; on failure no media is locked at all (all
 
11663
 * succeeded individual locks will be undone).
 
11664
 *
 
11665
 * This method is intended to be called when the machine is in Starting or
 
11666
 * Restoring state and asserts otherwise.
 
11667
 *
 
11668
 * The locks made by this method must be undone by calling #unlockMedia() when
 
11669
 * no more needed.
 
11670
 */
 
11671
HRESULT SessionMachine::lockMedia()
 
11672
{
 
11673
    AutoCaller autoCaller(this);
 
11674
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11675
 
 
11676
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11677
 
 
11678
    AssertReturn(   mData->mMachineState == MachineState_Starting
 
11679
                 || mData->mMachineState == MachineState_Restoring
 
11680
                 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
 
11681
    /* bail out if trying to lock things with already set up locking */
 
11682
    AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
 
11683
 
 
11684
    MultiResult mrc(S_OK);
 
11685
 
 
11686
    /* Collect locking information for all medium objects attached to the VM. */
 
11687
    for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
 
11688
         it != mMediaData->mAttachments.end();
 
11689
         ++it)
 
11690
    {
 
11691
        MediumAttachment* pAtt = *it;
 
11692
        DeviceType_T devType = pAtt->getType();
 
11693
        Medium *pMedium = pAtt->getMedium();
 
11694
 
 
11695
        MediumLockList *pMediumLockList(new MediumLockList());
 
11696
        // There can be attachments without a medium (floppy/dvd), and thus
 
11697
        // it's impossible to create a medium lock list. It still makes sense
 
11698
        // to have the empty medium lock list in the map in case a medium is
 
11699
        // attached later.
 
11700
        if (pMedium != NULL)
 
11701
        {
 
11702
            MediumType_T mediumType = pMedium->getType();
 
11703
            bool fIsReadOnlyLock =    mediumType == MediumType_Readonly
 
11704
                                   || mediumType == MediumType_Shareable;
 
11705
            bool fIsVitalImage = (devType == DeviceType_HardDisk);
 
11706
 
 
11707
            mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
 
11708
                                                !fIsReadOnlyLock /* fMediumLockWrite */,
 
11709
                                                NULL,
 
11710
                                                *pMediumLockList);
 
11711
            if (FAILED(mrc))
 
11712
            {
 
11713
                delete pMediumLockList;
 
11714
                mData->mSession.mLockedMedia.Clear();
 
11715
                break;
 
11716
            }
 
11717
        }
 
11718
 
 
11719
        HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
 
11720
        if (FAILED(rc))
 
11721
        {
 
11722
            mData->mSession.mLockedMedia.Clear();
 
11723
            mrc = setError(rc,
 
11724
                           tr("Collecting locking information for all attached media failed"));
 
11725
            break;
 
11726
        }
 
11727
    }
 
11728
 
 
11729
    if (SUCCEEDED(mrc))
 
11730
    {
 
11731
        /* Now lock all media. If this fails, nothing is locked. */
 
11732
        HRESULT rc = mData->mSession.mLockedMedia.Lock();
 
11733
        if (FAILED(rc))
 
11734
        {
 
11735
            mrc = setError(rc,
 
11736
                           tr("Locking of attached media failed"));
 
11737
        }
 
11738
    }
 
11739
 
 
11740
    return mrc;
 
11741
}
 
11742
 
 
11743
/**
 
11744
 * Undoes the locks made by by #lockMedia().
 
11745
 */
 
11746
void SessionMachine::unlockMedia()
 
11747
{
 
11748
    AutoCaller autoCaller(this);
 
11749
    AssertComRCReturnVoid(autoCaller.rc());
 
11750
 
 
11751
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11752
 
 
11753
    /* we may be holding important error info on the current thread;
 
11754
     * preserve it */
 
11755
    ErrorInfoKeeper eik;
 
11756
 
 
11757
    HRESULT rc = mData->mSession.mLockedMedia.Clear();
 
11758
    AssertComRC(rc);
 
11759
}
 
11760
 
 
11761
/**
 
11762
 * Helper to change the machine state (reimplementation).
 
11763
 *
 
11764
 * @note Locks this object for writing.
 
11765
 */
 
11766
HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
 
11767
{
 
11768
    LogFlowThisFuncEnter();
 
11769
    LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
 
11770
 
 
11771
    AutoCaller autoCaller(this);
 
11772
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11773
 
 
11774
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11775
 
 
11776
    MachineState_T oldMachineState = mData->mMachineState;
 
11777
 
 
11778
    AssertMsgReturn(oldMachineState != aMachineState,
 
11779
                    ("oldMachineState=%s, aMachineState=%s\n",
 
11780
                     Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
 
11781
                    E_FAIL);
 
11782
 
 
11783
    HRESULT rc = S_OK;
 
11784
 
 
11785
    int stsFlags = 0;
 
11786
    bool deleteSavedState = false;
 
11787
 
 
11788
    /* detect some state transitions */
 
11789
 
 
11790
    if (   (   oldMachineState == MachineState_Saved
 
11791
            && aMachineState   == MachineState_Restoring)
 
11792
        || (   (   oldMachineState == MachineState_PoweredOff
 
11793
                || oldMachineState == MachineState_Teleported
 
11794
                || oldMachineState == MachineState_Aborted
 
11795
               )
 
11796
            && (   aMachineState   == MachineState_TeleportingIn
 
11797
                || aMachineState   == MachineState_Starting
 
11798
               )
 
11799
           )
 
11800
       )
 
11801
    {
 
11802
        /* The EMT thread is about to start */
 
11803
 
 
11804
        /* Nothing to do here for now... */
 
11805
 
 
11806
        /// @todo NEWMEDIA don't let mDVDDrive and other children
 
11807
        /// change anything when in the Starting/Restoring state
 
11808
    }
 
11809
    else if (   (   oldMachineState == MachineState_Running
 
11810
                 || oldMachineState == MachineState_Paused
 
11811
                 || oldMachineState == MachineState_Teleporting
 
11812
                 || oldMachineState == MachineState_LiveSnapshotting
 
11813
                 || oldMachineState == MachineState_Stuck
 
11814
                 || oldMachineState == MachineState_Starting
 
11815
                 || oldMachineState == MachineState_Stopping
 
11816
                 || oldMachineState == MachineState_Saving
 
11817
                 || oldMachineState == MachineState_Restoring
 
11818
                 || oldMachineState == MachineState_TeleportingPausedVM
 
11819
                 || oldMachineState == MachineState_TeleportingIn
 
11820
                 )
 
11821
             && (   aMachineState == MachineState_PoweredOff
 
11822
                 || aMachineState == MachineState_Saved
 
11823
                 || aMachineState == MachineState_Teleported
 
11824
                 || aMachineState == MachineState_Aborted
 
11825
                )
 
11826
             /* ignore PoweredOff->Saving->PoweredOff transition when taking a
 
11827
              * snapshot */
 
11828
             && (   mConsoleTaskData.mSnapshot.isNull()
 
11829
                 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
 
11830
                )
 
11831
            )
 
11832
    {
 
11833
        /* The EMT thread has just stopped, unlock attached media. Note that as
 
11834
         * opposed to locking that is done from Console, we do unlocking here
 
11835
         * because the VM process may have aborted before having a chance to
 
11836
         * properly unlock all media it locked. */
 
11837
 
 
11838
        unlockMedia();
 
11839
    }
 
11840
 
 
11841
    if (oldMachineState == MachineState_Restoring)
 
11842
    {
 
11843
        if (aMachineState != MachineState_Saved)
 
11844
        {
 
11845
            /*
 
11846
             *  delete the saved state file once the machine has finished
 
11847
             *  restoring from it (note that Console sets the state from
 
11848
             *  Restoring to Saved if the VM couldn't restore successfully,
 
11849
             *  to give the user an ability to fix an error and retry --
 
11850
             *  we keep the saved state file in this case)
 
11851
             */
 
11852
            deleteSavedState = true;
 
11853
        }
 
11854
    }
 
11855
    else if (   oldMachineState == MachineState_Saved
 
11856
             && (   aMachineState == MachineState_PoweredOff
 
11857
                 || aMachineState == MachineState_Aborted
 
11858
                 || aMachineState == MachineState_Teleported
 
11859
                )
 
11860
            )
 
11861
    {
 
11862
        /*
 
11863
         *  delete the saved state after Console::ForgetSavedState() is called
 
11864
         *  or if the VM process (owning a direct VM session) crashed while the
 
11865
         *  VM was Saved
 
11866
         */
 
11867
 
 
11868
        /// @todo (dmik)
 
11869
        //      Not sure that deleting the saved state file just because of the
 
11870
        //      client death before it attempted to restore the VM is a good
 
11871
        //      thing. But when it crashes we need to go to the Aborted state
 
11872
        //      which cannot have the saved state file associated... The only
 
11873
        //      way to fix this is to make the Aborted condition not a VM state
 
11874
        //      but a bool flag: i.e., when a crash occurs, set it to true and
 
11875
        //      change the state to PoweredOff or Saved depending on the
 
11876
        //      saved state presence.
 
11877
 
 
11878
        deleteSavedState = true;
 
11879
        mData->mCurrentStateModified = TRUE;
 
11880
        stsFlags |= SaveSTS_CurStateModified;
 
11881
    }
 
11882
 
 
11883
    if (   aMachineState == MachineState_Starting
 
11884
        || aMachineState == MachineState_Restoring
 
11885
        || aMachineState == MachineState_TeleportingIn
 
11886
       )
 
11887
    {
 
11888
        /* set the current state modified flag to indicate that the current
 
11889
         * state is no more identical to the state in the
 
11890
         * current snapshot */
 
11891
        if (!mData->mCurrentSnapshot.isNull())
 
11892
        {
 
11893
            mData->mCurrentStateModified = TRUE;
 
11894
            stsFlags |= SaveSTS_CurStateModified;
 
11895
        }
 
11896
    }
 
11897
 
 
11898
    if (deleteSavedState)
 
11899
    {
 
11900
        if (mRemoveSavedState)
 
11901
        {
 
11902
            Assert(!mSSData->mStateFilePath.isEmpty());
 
11903
            RTFileDelete(mSSData->mStateFilePath.c_str());
 
11904
        }
 
11905
        mSSData->mStateFilePath.setNull();
 
11906
        stsFlags |= SaveSTS_StateFilePath;
 
11907
    }
 
11908
 
 
11909
    /* redirect to the underlying peer machine */
 
11910
    mPeer->setMachineState(aMachineState);
 
11911
 
 
11912
    if (   aMachineState == MachineState_PoweredOff
 
11913
        || aMachineState == MachineState_Teleported
 
11914
        || aMachineState == MachineState_Aborted
 
11915
        || aMachineState == MachineState_Saved)
 
11916
    {
 
11917
        /* the machine has stopped execution
 
11918
         * (or the saved state file was adopted) */
 
11919
        stsFlags |= SaveSTS_StateTimeStamp;
 
11920
    }
 
11921
 
 
11922
    if (   (   oldMachineState == MachineState_PoweredOff
 
11923
            || oldMachineState == MachineState_Aborted
 
11924
            || oldMachineState == MachineState_Teleported
 
11925
           )
 
11926
        && aMachineState == MachineState_Saved)
 
11927
    {
 
11928
        /* the saved state file was adopted */
 
11929
        Assert(!mSSData->mStateFilePath.isEmpty());
 
11930
        stsFlags |= SaveSTS_StateFilePath;
 
11931
    }
 
11932
 
 
11933
#ifdef VBOX_WITH_GUEST_PROPS
 
11934
    if (   aMachineState == MachineState_PoweredOff
 
11935
        || aMachineState == MachineState_Aborted
 
11936
        || aMachineState == MachineState_Teleported)
 
11937
    {
 
11938
        /* Make sure any transient guest properties get removed from the
 
11939
         * property store on shutdown. */
 
11940
 
 
11941
        HWData::GuestPropertyList::iterator it;
 
11942
        BOOL fNeedsSaving = mData->mGuestPropertiesModified;
 
11943
        if (!fNeedsSaving)
 
11944
            for (it = mHWData->mGuestProperties.begin();
 
11945
                 it != mHWData->mGuestProperties.end(); ++it)
 
11946
                if (it->mFlags & guestProp::TRANSIENT)
 
11947
                {
 
11948
                    fNeedsSaving = true;
 
11949
                    break;
 
11950
                }
 
11951
        if (fNeedsSaving)
 
11952
        {
 
11953
            mData->mCurrentStateModified = TRUE;
 
11954
            stsFlags |= SaveSTS_CurStateModified;
 
11955
            SaveSettings();     // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings?
 
11956
        }
 
11957
    }
 
11958
#endif
 
11959
 
 
11960
    rc = saveStateSettings(stsFlags);
 
11961
 
 
11962
    if (   (   oldMachineState != MachineState_PoweredOff
 
11963
            && oldMachineState != MachineState_Aborted
 
11964
            && oldMachineState != MachineState_Teleported
 
11965
           )
 
11966
        && (   aMachineState == MachineState_PoweredOff
 
11967
            || aMachineState == MachineState_Aborted
 
11968
            || aMachineState == MachineState_Teleported
 
11969
           )
 
11970
       )
 
11971
    {
 
11972
        /* we've been shut down for any reason */
 
11973
        /* no special action so far */
 
11974
    }
 
11975
 
 
11976
    LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
 
11977
    LogFlowThisFuncLeave();
 
11978
    return rc;
 
11979
}
 
11980
 
 
11981
/**
 
11982
 *  Sends the current machine state value to the VM process.
 
11983
 *
 
11984
 *  @note Locks this object for reading, then calls a client process.
 
11985
 */
 
11986
HRESULT SessionMachine::updateMachineStateOnClient()
 
11987
{
 
11988
    AutoCaller autoCaller(this);
 
11989
    AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
 
11990
 
 
11991
    ComPtr<IInternalSessionControl> directControl;
 
11992
    {
 
11993
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
11994
        AssertReturn(!!mData, E_FAIL);
 
11995
        directControl = mData->mSession.mDirectControl;
 
11996
 
 
11997
        /* directControl may be already set to NULL here in #OnSessionEnd()
 
11998
         * called too early by the direct session process while there is still
 
11999
         * some operation (like deleting the snapshot) in progress. The client
 
12000
         * process in this case is waiting inside Session::close() for the
 
12001
         * "end session" process object to complete, while #uninit() called by
 
12002
         * #checkForDeath() on the Watcher thread is waiting for the pending
 
12003
         * operation to complete. For now, we accept this inconsistent behavior
 
12004
         * and simply do nothing here. */
 
12005
 
 
12006
        if (mData->mSession.mState == SessionState_Unlocking)
 
12007
            return S_OK;
 
12008
 
 
12009
        AssertReturn(!directControl.isNull(), E_FAIL);
 
12010
    }
 
12011
 
 
12012
    return directControl->UpdateMachineState(mData->mMachineState);
 
12013
}