~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/VBox/Main/VirtualBoxImpl.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 *
 
3
 * VirtualBox COM class implementation
 
4
 */
 
5
 
 
6
/*
 
7
 * Copyright (C) 2006-2007 innotek GmbH
 
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 as published by the Free Software Foundation,
 
13
 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
 
14
 * distribution. VirtualBox OSE is distributed in the hope that it will
 
15
 * be useful, but WITHOUT ANY WARRANTY of any kind.
 
16
 */
 
17
 
 
18
#include "VirtualBoxImpl.h"
 
19
#include "MachineImpl.h"
 
20
#include "HardDiskImpl.h"
 
21
#include "DVDImageImpl.h"
 
22
#include "FloppyImageImpl.h"
 
23
#include "SharedFolderImpl.h"
 
24
#include "ProgressImpl.h"
 
25
#include "HostImpl.h"
 
26
#include "USBControllerImpl.h"
 
27
#include "SystemPropertiesImpl.h"
 
28
#include "Logging.h"
 
29
 
 
30
#include "GuestOSTypeImpl.h"
 
31
 
 
32
#ifdef RT_OS_WINDOWS
 
33
#include "win32/svchlp.h"
 
34
#endif
 
35
 
 
36
#include <stdio.h>
 
37
#include <stdlib.h>
 
38
 
 
39
#include <iprt/path.h>
 
40
#include <iprt/dir.h>
 
41
#include <iprt/file.h>
 
42
#include <iprt/string.h>
 
43
#include <iprt/uuid.h>
 
44
#include <iprt/thread.h>
 
45
#include <iprt/process.h>
 
46
#include <iprt/cpputils.h>
 
47
 
 
48
#include <VBox/err.h>
 
49
#include <VBox/param.h>
 
50
#include <VBox/VBoxHDD.h>
 
51
#include <VBox/VBoxHDD-new.h>
 
52
#include <VBox/ostypes.h>
 
53
#include <VBox/version.h>
 
54
 
 
55
#include <VBox/com/com.h>
 
56
 
 
57
#include <algorithm>
 
58
#include <set>
 
59
#include <memory> // for auto_ptr
 
60
 
 
61
// defines
 
62
/////////////////////////////////////////////////////////////////////////////
 
63
 
 
64
#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
 
65
 
 
66
// globals
 
67
/////////////////////////////////////////////////////////////////////////////
 
68
 
 
69
static const char DefaultGlobalConfig [] =
 
70
{
 
71
    "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" RTFILE_LINEFEED
 
72
    "<!-- innotek VirtualBox Global Configuration -->" RTFILE_LINEFEED
 
73
    "<VirtualBox xmlns=\"" VBOX_XML_NAMESPACE "\" "
 
74
        "version=\"" VBOX_XML_VERSION "-" VBOX_XML_PLATFORM "\">" RTFILE_LINEFEED
 
75
    "  <Global>"RTFILE_LINEFEED
 
76
    "    <MachineRegistry/>"RTFILE_LINEFEED
 
77
    "    <DiskRegistry/>"RTFILE_LINEFEED
 
78
    "    <USBDeviceFilters/>"RTFILE_LINEFEED
 
79
    "    <SystemProperties/>"RTFILE_LINEFEED
 
80
    "  </Global>"RTFILE_LINEFEED
 
81
    "</VirtualBox>"RTFILE_LINEFEED
 
82
};
 
83
 
 
84
// static
 
85
Bstr VirtualBox::sVersion;
 
86
 
 
87
// constructor / destructor
 
88
/////////////////////////////////////////////////////////////////////////////
 
89
 
 
90
VirtualBox::VirtualBox()
 
91
    : mAsyncEventThread (NIL_RTTHREAD)
 
92
    , mAsyncEventQ (NULL)
 
93
{}
 
94
 
 
95
VirtualBox::~VirtualBox() {}
 
96
 
 
97
HRESULT VirtualBox::FinalConstruct()
 
98
{
 
99
    LogFlowThisFunc (("\n"));
 
100
 
 
101
    return init();
 
102
}
 
103
 
 
104
void VirtualBox::FinalRelease()
 
105
{
 
106
    LogFlowThisFunc (("\n"));
 
107
 
 
108
    uninit();
 
109
}
 
110
 
 
111
VirtualBox::Data::Data()
 
112
{
 
113
}
 
114
 
 
115
// public initializer/uninitializer for internal purposes only
 
116
/////////////////////////////////////////////////////////////////////////////
 
117
 
 
118
/**
 
119
 *  Initializes the VirtualBox object.
 
120
 *
 
121
 *  @return COM result code
 
122
 */
 
123
HRESULT VirtualBox::init()
 
124
{
 
125
    /* Enclose the state transition NotReady->InInit->Ready */
 
126
    AutoInitSpan autoInitSpan (this);
 
127
    AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
 
128
 
 
129
    LogFlow (("===========================================================\n"));
 
130
    LogFlowThisFuncEnter();
 
131
 
 
132
    if (sVersion.isNull())
 
133
        sVersion = VBOX_VERSION_STRING;
 
134
    LogFlowThisFunc (("Version: %ls\n", sVersion.raw()));
 
135
 
 
136
    /* Get the VirtualBox home directory. */
 
137
    {
 
138
        char homeDir [RTPATH_MAX];
 
139
        int vrc = com::GetVBoxUserHomeDirectory (homeDir, sizeof (homeDir));
 
140
        if (VBOX_FAILURE (vrc))
 
141
            return setError (E_FAIL,
 
142
                tr ("Could not create the VirtualBox home directory '%s'"
 
143
                    "(%Vrc)"),
 
144
                homeDir, vrc);
 
145
 
 
146
        unconst (mData.mHomeDir) = homeDir;
 
147
    }
 
148
 
 
149
    /* compose the global config file name (always full path) */
 
150
    Utf8StrFmt vboxConfigFile ("%s%c%s", mData.mHomeDir.raw(),
 
151
                               RTPATH_DELIMITER, VBOX_GLOBAL_SETTINGS_FILE);
 
152
 
 
153
    /* store the config file name */
 
154
    unconst (mData.mCfgFile.mName) = vboxConfigFile;
 
155
 
 
156
    /* lock the config file */
 
157
    HRESULT rc = lockConfig();
 
158
    if (SUCCEEDED (rc))
 
159
    {
 
160
        if (!isConfigLocked())
 
161
        {
 
162
            /*
 
163
             *  This means the config file not found. This is not fatal --
 
164
             *  we just create an empty one.
 
165
             */
 
166
            RTFILE handle = NIL_RTFILE;
 
167
            int vrc = RTFileOpen (&handle, vboxConfigFile,
 
168
                                  RTFILE_O_READWRITE | RTFILE_O_CREATE |
 
169
                                  RTFILE_O_DENY_WRITE);
 
170
            if (VBOX_SUCCESS (vrc))
 
171
                vrc = RTFileWrite (handle,
 
172
                                   (void *) DefaultGlobalConfig,
 
173
                                   sizeof (DefaultGlobalConfig), NULL);
 
174
            if (VBOX_FAILURE (vrc))
 
175
            {
 
176
                rc = setError (E_FAIL, tr ("Could not create the default settings file "
 
177
                                           "'%s' (%Vrc)"),
 
178
                                       vboxConfigFile.raw(), vrc);
 
179
            }
 
180
            else
 
181
            {
 
182
                mData.mCfgFile.mHandle = handle;
 
183
                /* we do not close the file to simulate lockConfig() */
 
184
            }
 
185
        }
 
186
    }
 
187
 
 
188
    /* initialize our Xerces XML subsystem */
 
189
    if (SUCCEEDED (rc))
 
190
    {
 
191
        int vrc = CFGLDRInitialize();
 
192
        if (VBOX_FAILURE (vrc))
 
193
            rc = setError (E_FAIL, tr ("Could not initialize the XML parser (%Vrc)"),
 
194
                                   vrc);
 
195
    }
 
196
 
 
197
    if (SUCCEEDED (rc))
 
198
    {
 
199
        CFGHANDLE configLoader = NULL;
 
200
        char *loaderError = NULL;
 
201
 
 
202
        /* load the config file */
 
203
        int vrc = CFGLDRLoad (&configLoader, vboxConfigFile, mData.mCfgFile.mHandle,
 
204
                              XmlSchemaNS, true, cfgLdrEntityResolver,
 
205
                              &loaderError);
 
206
        if (VBOX_SUCCESS (vrc))
 
207
        {
 
208
            CFGNODE global = NULL;
 
209
            CFGLDRGetNode (configLoader, "VirtualBox/Global", 0, &global);
 
210
            Assert (global);
 
211
 
 
212
            do
 
213
            {
 
214
                /* create the host object early, machines will need it */
 
215
                unconst (mData.mHost).createObject();
 
216
                rc = mData.mHost->init (this);
 
217
                ComAssertComRCBreak (rc, rc = rc);
 
218
 
 
219
                unconst (mData.mSystemProperties).createObject();
 
220
                rc = mData.mSystemProperties->init (this);
 
221
                ComAssertComRCBreak (rc, rc = rc);
 
222
 
 
223
                rc = loadDisks (global);
 
224
                CheckComRCBreakRC ((rc));
 
225
 
 
226
                /* guest OS type objects, needed by the machines */
 
227
                rc = registerGuestOSTypes();
 
228
                ComAssertComRCBreak (rc, rc = rc);
 
229
 
 
230
                /* machines */
 
231
                rc = loadMachines (global);
 
232
                CheckComRCBreakRC ((rc));
 
233
 
 
234
                /* host data (USB filters) */
 
235
                rc = mData.mHost->loadSettings (global);
 
236
                CheckComRCBreakRC ((rc));
 
237
 
 
238
                rc = mData.mSystemProperties->loadSettings (global);
 
239
                CheckComRCBreakRC ((rc));
 
240
 
 
241
                /* check hard disk consistency */
 
242
/// @todo (r=dmik) add IVirtualBox::cleanupHardDisks() instead or similar
 
243
//                for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
 
244
//                     it != mData.mHardDisks.end() && SUCCEEDED (rc);
 
245
//                     ++ it)
 
246
//                {
 
247
//                    rc = (*it)->checkConsistency();
 
248
//                }
 
249
//                CheckComRCBreakRC ((rc));
 
250
            }
 
251
            while (0);
 
252
 
 
253
            /// @todo (dmik) if successful, check for orphan (unused) diffs
 
254
            //  that might be left because of the server crash, and remove them.
 
255
 
 
256
            CFGLDRReleaseNode (global);
 
257
            CFGLDRFree(configLoader);
 
258
        }
 
259
        else
 
260
        {
 
261
            rc = setError (E_FAIL,
 
262
                tr ("Could not load the settings file '%ls' (%Vrc)%s%s"),
 
263
                mData.mCfgFile.mName.raw(), vrc,
 
264
                loaderError ? ".\n" : "", loaderError ? loaderError : "");
 
265
        }
 
266
 
 
267
        if (loaderError)
 
268
            RTMemTmpFree (loaderError);
 
269
    }
 
270
 
 
271
    if (SUCCEEDED (rc))
 
272
    {
 
273
        /* start the client watcher thread */
 
274
#if defined(RT_OS_WINDOWS)
 
275
        unconst (mWatcherData.mUpdateReq) = ::CreateEvent (NULL, FALSE, FALSE, NULL);
 
276
#elif defined(RT_OS_OS2)
 
277
        RTSemEventCreate (&unconst (mWatcherData.mUpdateReq));
 
278
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
279
        RTSemEventCreate (&unconst (mWatcherData.mUpdateReq));
 
280
#else
 
281
# error "Port me!"
 
282
#endif
 
283
        int vrc = RTThreadCreate (&unconst (mWatcherData.mThread),
 
284
                                  ClientWatcher, (void *) this,
 
285
                                  0, RTTHREADTYPE_MAIN_WORKER,
 
286
                                  RTTHREADFLAGS_WAITABLE, "Watcher");
 
287
        ComAssertRC (vrc);
 
288
        if (VBOX_FAILURE (vrc))
 
289
            rc = E_FAIL;
 
290
    }
 
291
 
 
292
    if (SUCCEEDED (rc)) do
 
293
    {
 
294
        /* start the async event handler thread */
 
295
        int vrc = RTThreadCreate (&unconst (mAsyncEventThread), AsyncEventHandler,
 
296
                                  &unconst (mAsyncEventQ),
 
297
                                  0, RTTHREADTYPE_MAIN_WORKER,
 
298
                                  RTTHREADFLAGS_WAITABLE, "EventHandler");
 
299
        ComAssertRCBreak (vrc, rc = E_FAIL);
 
300
 
 
301
        /* wait until the thread sets mAsyncEventQ */
 
302
        RTThreadUserWait (mAsyncEventThread, RT_INDEFINITE_WAIT);
 
303
        ComAssertBreak (mAsyncEventQ, rc = E_FAIL);
 
304
    }
 
305
    while (0);
 
306
 
 
307
    /* Confirm a successful initialization when it's the case */
 
308
    if (SUCCEEDED (rc))
 
309
        autoInitSpan.setSucceeded();
 
310
 
 
311
    LogFlowThisFunc (("rc=%08X\n", rc));
 
312
    LogFlowThisFuncLeave();
 
313
    LogFlow (("===========================================================\n"));
 
314
    return rc;
 
315
}
 
316
 
 
317
void VirtualBox::uninit()
 
318
{
 
319
    /* Enclose the state transition Ready->InUninit->NotReady */
 
320
    AutoUninitSpan autoUninitSpan (this);
 
321
    if (autoUninitSpan.uninitDone())
 
322
        return;
 
323
 
 
324
    LogFlow (("===========================================================\n"));
 
325
    LogFlowThisFuncEnter();
 
326
    LogFlowThisFunc (("initFailed()=%d\n", autoUninitSpan.initFailed()));
 
327
 
 
328
    /* tell all our child objects we've been uninitialized */
 
329
 
 
330
    LogFlowThisFunc (("Uninitializing machines (%d)...\n", mData.mMachines.size()));
 
331
    if (mData.mMachines.size())
 
332
    {
 
333
        MachineList::iterator it = mData.mMachines.begin();
 
334
        while (it != mData.mMachines.end())
 
335
            (*it++)->uninit();
 
336
        mData.mMachines.clear();
 
337
    }
 
338
 
 
339
    if (mData.mSystemProperties)
 
340
    {
 
341
        mData.mSystemProperties->uninit();
 
342
        unconst (mData.mSystemProperties).setNull();
 
343
    }
 
344
 
 
345
    if (mData.mHost)
 
346
    {
 
347
        mData.mHost->uninit();
 
348
        unconst (mData.mHost).setNull();
 
349
    }
 
350
 
 
351
    /*
 
352
     *  Uninit all other children still referenced by clients
 
353
     *  (unregistered machines, hard disks, DVD/floppy images,
 
354
     *  server-side progress operations).
 
355
     */
 
356
    uninitDependentChildren();
 
357
 
 
358
    mData.mFloppyImages.clear();
 
359
    mData.mDVDImages.clear();
 
360
    mData.mHardDisks.clear();
 
361
 
 
362
    mData.mHardDiskMap.clear();
 
363
 
 
364
    mData.mProgressOperations.clear();
 
365
 
 
366
    mData.mGuestOSTypes.clear();
 
367
 
 
368
    /* unlock the config file */
 
369
    unlockConfig();
 
370
 
 
371
    LogFlowThisFunc (("Releasing callbacks...\n"));
 
372
    if (mData.mCallbacks.size())
 
373
    {
 
374
        /* release all callbacks */
 
375
        LogWarningFunc (("%d unregistered callbacks!\n",
 
376
                         mData.mCallbacks.size()));
 
377
        mData.mCallbacks.clear();
 
378
    }
 
379
 
 
380
    LogFlowThisFunc (("Terminating the async event handler...\n"));
 
381
    if (mAsyncEventThread != NIL_RTTHREAD)
 
382
    {
 
383
        /* signal to exit the event loop */
 
384
        if (mAsyncEventQ->postEvent (NULL))
 
385
        {
 
386
            /*
 
387
             *  Wait for thread termination (only if we've successfully posted
 
388
             *  a NULL event!)
 
389
             */
 
390
            int vrc = RTThreadWait (mAsyncEventThread, 60000, NULL);
 
391
            if (VBOX_FAILURE (vrc))
 
392
                LogWarningFunc (("RTThreadWait(%RTthrd) -> %Vrc\n",
 
393
                                 mAsyncEventThread, vrc));
 
394
        }
 
395
        else
 
396
        {
 
397
            AssertMsgFailed (("postEvent(NULL) failed\n"));
 
398
            RTThreadWait (mAsyncEventThread, 0, NULL);
 
399
        }
 
400
 
 
401
        unconst (mAsyncEventThread) = NIL_RTTHREAD;
 
402
        unconst (mAsyncEventQ) = NULL;
 
403
    }
 
404
 
 
405
    LogFlowThisFunc (("Terminating the client watcher...\n"));
 
406
    if (mWatcherData.mThread != NIL_RTTHREAD)
 
407
    {
 
408
        /* signal the client watcher thread */
 
409
        updateClientWatcher();
 
410
        /* wait for the termination */
 
411
        RTThreadWait (mWatcherData.mThread, RT_INDEFINITE_WAIT, NULL);
 
412
        unconst (mWatcherData.mThread) = NIL_RTTHREAD;
 
413
    }
 
414
    mWatcherData.mProcesses.clear();
 
415
#if defined(RT_OS_WINDOWS)
 
416
    if (mWatcherData.mUpdateReq != NULL)
 
417
    {
 
418
        ::CloseHandle (mWatcherData.mUpdateReq);
 
419
        unconst (mWatcherData.mUpdateReq) = NULL;
 
420
    }
 
421
#elif defined(RT_OS_OS2)
 
422
    if (mWatcherData.mUpdateReq != NIL_RTSEMEVENT)
 
423
    {
 
424
        RTSemEventDestroy (mWatcherData.mUpdateReq);
 
425
        unconst (mWatcherData.mUpdateReq) = NIL_RTSEMEVENT;
 
426
    }
 
427
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
428
    if (mWatcherData.mUpdateReq != NIL_RTSEMEVENT)
 
429
    {
 
430
        RTSemEventDestroy (mWatcherData.mUpdateReq);
 
431
        unconst (mWatcherData.mUpdateReq) = NIL_RTSEMEVENT;
 
432
    }
 
433
#else
 
434
# error "Port me!"
 
435
#endif
 
436
 
 
437
    /* uninitialize the Xerces XML subsystem */
 
438
    CFGLDRShutdown();
 
439
 
 
440
    LogFlowThisFuncLeave();
 
441
    LogFlow (("===========================================================\n"));
 
442
}
 
443
 
 
444
// IVirtualBox properties
 
445
/////////////////////////////////////////////////////////////////////////////
 
446
 
 
447
STDMETHODIMP VirtualBox::COMGETTER(Version) (BSTR *aVersion)
 
448
{
 
449
    if (!aVersion)
 
450
        return E_INVALIDARG;
 
451
 
 
452
    AutoCaller autoCaller (this);
 
453
    CheckComRCReturnRC (autoCaller.rc());
 
454
 
 
455
    sVersion.cloneTo (aVersion);
 
456
    return S_OK;
 
457
}
 
458
 
 
459
STDMETHODIMP VirtualBox::COMGETTER(HomeFolder) (BSTR *aHomeFolder)
 
460
{
 
461
    if (!aHomeFolder)
 
462
        return E_POINTER;
 
463
 
 
464
    AutoCaller autoCaller (this);
 
465
    CheckComRCReturnRC (autoCaller.rc());
 
466
 
 
467
    mData.mHomeDir.cloneTo (aHomeFolder);
 
468
    return S_OK;
 
469
}
 
470
 
 
471
STDMETHODIMP VirtualBox::COMGETTER(Host) (IHost **aHost)
 
472
{
 
473
    if (!aHost)
 
474
        return E_POINTER;
 
475
 
 
476
    AutoCaller autoCaller (this);
 
477
    CheckComRCReturnRC (autoCaller.rc());
 
478
 
 
479
    mData.mHost.queryInterfaceTo (aHost);
 
480
    return S_OK;
 
481
}
 
482
 
 
483
STDMETHODIMP VirtualBox::COMGETTER(SystemProperties) (ISystemProperties **aSystemProperties)
 
484
{
 
485
    if (!aSystemProperties)
 
486
        return E_POINTER;
 
487
 
 
488
    AutoCaller autoCaller (this);
 
489
    CheckComRCReturnRC (autoCaller.rc());
 
490
 
 
491
    mData.mSystemProperties.queryInterfaceTo (aSystemProperties);
 
492
    return S_OK;
 
493
}
 
494
 
 
495
/** @note Locks this object for reading. */
 
496
STDMETHODIMP VirtualBox::COMGETTER(Machines) (IMachineCollection **aMachines)
 
497
{
 
498
    if (!aMachines)
 
499
        return E_POINTER;
 
500
 
 
501
    AutoCaller autoCaller (this);
 
502
    CheckComRCReturnRC (autoCaller.rc());
 
503
 
 
504
    ComObjPtr <MachineCollection> collection;
 
505
    collection.createObject();
 
506
 
 
507
    AutoReaderLock alock (this);
 
508
    collection->init (mData.mMachines);
 
509
    collection.queryInterfaceTo (aMachines);
 
510
 
 
511
    return S_OK;
 
512
}
 
513
 
 
514
/** @note Locks this object for reading. */
 
515
STDMETHODIMP VirtualBox::COMGETTER(HardDisks) (IHardDiskCollection **aHardDisks)
 
516
{
 
517
    if (!aHardDisks)
 
518
        return E_POINTER;
 
519
 
 
520
    AutoCaller autoCaller (this);
 
521
    CheckComRCReturnRC (autoCaller.rc());
 
522
 
 
523
    ComObjPtr <HardDiskCollection> collection;
 
524
    collection.createObject();
 
525
 
 
526
    AutoReaderLock alock (this);
 
527
    collection->init (mData.mHardDisks);
 
528
    collection.queryInterfaceTo (aHardDisks);
 
529
 
 
530
    return S_OK;
 
531
}
 
532
 
 
533
/** @note Locks this object for reading. */
 
534
STDMETHODIMP VirtualBox::COMGETTER(DVDImages) (IDVDImageCollection **aDVDImages)
 
535
{
 
536
    if (!aDVDImages)
 
537
        return E_POINTER;
 
538
 
 
539
    AutoCaller autoCaller (this);
 
540
    CheckComRCReturnRC (autoCaller.rc());
 
541
 
 
542
    ComObjPtr <DVDImageCollection> collection;
 
543
    collection.createObject();
 
544
 
 
545
    AutoReaderLock alock (this);
 
546
    collection->init (mData.mDVDImages);
 
547
    collection.queryInterfaceTo (aDVDImages);
 
548
 
 
549
    return S_OK;
 
550
}
 
551
 
 
552
/** @note Locks this object for reading. */
 
553
STDMETHODIMP VirtualBox::COMGETTER(FloppyImages) (IFloppyImageCollection **aFloppyImages)
 
554
{
 
555
    if (!aFloppyImages)
 
556
        return E_POINTER;
 
557
 
 
558
    AutoCaller autoCaller (this);
 
559
    CheckComRCReturnRC (autoCaller.rc());
 
560
 
 
561
    ComObjPtr <FloppyImageCollection> collection;
 
562
    collection.createObject();
 
563
 
 
564
    AutoReaderLock alock (this);
 
565
    collection->init (mData.mFloppyImages);
 
566
    collection.queryInterfaceTo (aFloppyImages);
 
567
 
 
568
    return S_OK;
 
569
}
 
570
 
 
571
/** @note Locks this object for reading. */
 
572
STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations) (IProgressCollection **aOperations)
 
573
{
 
574
    if (!aOperations)
 
575
        return E_POINTER;
 
576
 
 
577
    AutoCaller autoCaller (this);
 
578
    CheckComRCReturnRC (autoCaller.rc());
 
579
 
 
580
    ComObjPtr <ProgressCollection> collection;
 
581
    collection.createObject();
 
582
 
 
583
    AutoReaderLock alock (this);
 
584
    collection->init (mData.mProgressOperations);
 
585
    collection.queryInterfaceTo (aOperations);
 
586
 
 
587
    return S_OK;
 
588
}
 
589
 
 
590
/** @note Locks this object for reading. */
 
591
STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes) (IGuestOSTypeCollection **aGuestOSTypes)
 
592
{
 
593
    if (!aGuestOSTypes)
 
594
        return E_POINTER;
 
595
 
 
596
    AutoCaller autoCaller (this);
 
597
    CheckComRCReturnRC (autoCaller.rc());
 
598
 
 
599
    ComObjPtr <GuestOSTypeCollection> collection;
 
600
    collection.createObject();
 
601
 
 
602
    AutoReaderLock alock (this);
 
603
    collection->init (mData.mGuestOSTypes);
 
604
    collection.queryInterfaceTo (aGuestOSTypes);
 
605
 
 
606
    return S_OK;
 
607
}
 
608
 
 
609
STDMETHODIMP
 
610
VirtualBox::COMGETTER(SharedFolders) (ISharedFolderCollection **aSharedFolders)
 
611
{
 
612
    if (!aSharedFolders)
 
613
        return E_POINTER;
 
614
 
 
615
    AutoCaller autoCaller (this);
 
616
    CheckComRCReturnRC (autoCaller.rc());
 
617
 
 
618
    return setError (E_NOTIMPL, "Not yet implemented");
 
619
}
 
620
 
 
621
// IVirtualBox methods
 
622
/////////////////////////////////////////////////////////////////////////////
 
623
 
 
624
/** @note Locks mSystemProperties object for reading. */
 
625
STDMETHODIMP VirtualBox::CreateMachine (INPTR BSTR aBaseFolder,
 
626
                                        INPTR BSTR aName,
 
627
                                        IMachine **aMachine)
 
628
{
 
629
    LogFlowThisFuncEnter();
 
630
    LogFlowThisFunc (("aBaseFolder='%ls', aName='%ls' aMachine={%p}\n",
 
631
                      aBaseFolder, aName, aMachine));
 
632
 
 
633
    if (!aName)
 
634
        return E_INVALIDARG;
 
635
    if (!aMachine)
 
636
        return E_POINTER;
 
637
 
 
638
    if (!*aName)
 
639
        return setError (E_INVALIDARG,
 
640
            tr ("Machine name cannot be empty"));
 
641
 
 
642
    AutoCaller autoCaller (this);
 
643
    CheckComRCReturnRC (autoCaller.rc());
 
644
 
 
645
    /* Compose the settings file name using the following scheme:
 
646
     *
 
647
     *     <base_folder>/<machine_name>/<machine_name>.xml
 
648
     *
 
649
     * If a non-null and non-empty base folder is specified, the default
 
650
     * machine folder will be used as a base folder.
 
651
     */
 
652
    Bstr settingsFile = aBaseFolder;
 
653
    if (settingsFile.isEmpty())
 
654
    {
 
655
        AutoReaderLock propsLock (systemProperties());
 
656
        /* we use the non-full folder value below to keep the path relative */
 
657
        settingsFile = systemProperties()->defaultMachineFolder();
 
658
    }
 
659
    settingsFile = Utf8StrFmt ("%ls%c%ls%c%ls.xml",
 
660
                               settingsFile.raw(), RTPATH_DELIMITER,
 
661
                               aName, RTPATH_DELIMITER, aName);
 
662
 
 
663
    HRESULT rc = E_FAIL;
 
664
 
 
665
    /* create a new object */
 
666
    ComObjPtr <Machine> machine;
 
667
    rc = machine.createObject();
 
668
    if (SUCCEEDED (rc))
 
669
    {
 
670
        /* initialize the machine object */
 
671
        rc = machine->init (this, settingsFile, Machine::Init_New, aName);
 
672
        if (SUCCEEDED (rc))
 
673
        {
 
674
            /* set the return value */
 
675
            rc = machine.queryInterfaceTo (aMachine);
 
676
            ComAssertComRC (rc);
 
677
        }
 
678
    }
 
679
 
 
680
    LogFlowThisFunc (("rc=%08X\n", rc));
 
681
    LogFlowThisFuncLeave();
 
682
 
 
683
    return rc;
 
684
}
 
685
 
 
686
STDMETHODIMP VirtualBox::CreateLegacyMachine (INPTR BSTR aSettingsFile,
 
687
                                              INPTR BSTR aName,
 
688
                                              IMachine **aMachine)
 
689
{
 
690
    /* null and empty strings are not allowed as path names */
 
691
    if (!aSettingsFile || !(*aSettingsFile))
 
692
        return E_INVALIDARG;
 
693
 
 
694
    if (!aName)
 
695
        return E_INVALIDARG;
 
696
    if (!aMachine)
 
697
        return E_POINTER;
 
698
 
 
699
    if (!*aName)
 
700
        return setError (E_INVALIDARG,
 
701
            tr ("Machine name cannot be empty"));
 
702
 
 
703
    AutoCaller autoCaller (this);
 
704
    CheckComRCReturnRC (autoCaller.rc());
 
705
 
 
706
    HRESULT rc = E_FAIL;
 
707
 
 
708
    Utf8Str settingsFile = aSettingsFile;
 
709
    /* append the default extension if none */
 
710
    if (!RTPathHaveExt (settingsFile))
 
711
        settingsFile = Utf8StrFmt ("%s.xml", settingsFile.raw());
 
712
 
 
713
    /* create a new object */
 
714
    ComObjPtr<Machine> machine;
 
715
    rc = machine.createObject();
 
716
    if (SUCCEEDED (rc))
 
717
    {
 
718
        /* initialize the machine object */
 
719
        rc = machine->init (this, Bstr (settingsFile), Machine::Init_New,
 
720
                            aName, FALSE /* aNameSync */);
 
721
        if (SUCCEEDED (rc))
 
722
        {
 
723
            /* set the return value */
 
724
            rc = machine.queryInterfaceTo (aMachine);
 
725
            ComAssertComRC (rc);
 
726
        }
 
727
    }
 
728
    return rc;
 
729
}
 
730
 
 
731
STDMETHODIMP VirtualBox::OpenMachine (INPTR BSTR aSettingsFile,
 
732
                                      IMachine **aMachine)
 
733
{
 
734
    /* null and empty strings are not allowed as path names */
 
735
    if (!aSettingsFile || !(*aSettingsFile))
 
736
        return E_INVALIDARG;
 
737
 
 
738
    if (!aMachine)
 
739
        return E_POINTER;
 
740
 
 
741
    AutoCaller autoCaller (this);
 
742
    CheckComRCReturnRC (autoCaller.rc());
 
743
 
 
744
    HRESULT rc = E_FAIL;
 
745
 
 
746
    /* create a new object */
 
747
    ComObjPtr<Machine> machine;
 
748
    rc = machine.createObject();
 
749
    if (SUCCEEDED (rc))
 
750
    {
 
751
        /* initialize the machine object */
 
752
        rc = machine->init (this, aSettingsFile, Machine::Init_Existing);
 
753
        if (SUCCEEDED (rc))
 
754
        {
 
755
            /* set the return value */
 
756
            rc = machine.queryInterfaceTo (aMachine);
 
757
            ComAssertComRC (rc);
 
758
        }
 
759
    }
 
760
 
 
761
    return rc;
 
762
}
 
763
 
 
764
/** @note Locks objects! */
 
765
STDMETHODIMP VirtualBox::RegisterMachine (IMachine *aMachine)
 
766
{
 
767
    if (!aMachine)
 
768
        return E_INVALIDARG;
 
769
 
 
770
    AutoCaller autoCaller (this);
 
771
    CheckComRCReturnRC (autoCaller.rc());
 
772
 
 
773
    HRESULT rc;
 
774
 
 
775
    Bstr name;
 
776
    rc = aMachine->COMGETTER(Name) (name.asOutParam());
 
777
    CheckComRCReturnRC (rc);
 
778
 
 
779
    /*
 
780
     *  we can safely cast child to Machine * here because only Machine
 
781
     *  implementations of IMachine can be among our children
 
782
     */
 
783
    Machine *machine = static_cast <Machine *> (getDependentChild (aMachine));
 
784
    if (!machine)
 
785
    {
 
786
        /*
 
787
         *  this machine was not created by CreateMachine()
 
788
         *  or opened by OpenMachine() or loaded during startup
 
789
         */
 
790
        return setError (E_FAIL,
 
791
            tr ("The machine named '%ls' is not created within this "
 
792
                "VirtualBox instance"), name.raw());
 
793
    }
 
794
 
 
795
    /*
 
796
     *  Machine::trySetRegistered() will commit and save machine settings, and
 
797
     *  will also call #registerMachine() on success.
 
798
     */
 
799
    rc = registerMachine (machine);
 
800
 
 
801
    /* fire an event */
 
802
    if (SUCCEEDED (rc))
 
803
        onMachineRegistered (machine->data()->mUuid, TRUE);
 
804
 
 
805
    return rc;
 
806
}
 
807
 
 
808
/** @note Locks objects! */
 
809
STDMETHODIMP VirtualBox::GetMachine (INPTR GUIDPARAM aId, IMachine **aMachine)
 
810
{
 
811
    if (!aMachine)
 
812
        return E_POINTER;
 
813
 
 
814
    AutoCaller autoCaller (this);
 
815
    CheckComRCReturnRC (autoCaller.rc());
 
816
 
 
817
    ComObjPtr <Machine> machine;
 
818
    HRESULT rc = findMachine (Guid (aId), true /* setError */, &machine);
 
819
 
 
820
    /* the below will set *aMachine to NULL if machine is null */
 
821
    machine.queryInterfaceTo (aMachine);
 
822
 
 
823
    return rc;
 
824
}
 
825
 
 
826
/** @note Locks this object for reading, then some machine objects for reading. */
 
827
STDMETHODIMP VirtualBox::FindMachine (INPTR BSTR aName, IMachine **aMachine)
 
828
{
 
829
    LogFlowThisFuncEnter();
 
830
    LogFlowThisFunc (("aName=\"%ls\", aMachine={%p}\n", aName, aMachine));
 
831
 
 
832
    if (!aName)
 
833
        return E_INVALIDARG;
 
834
    if (!aMachine)
 
835
        return E_POINTER;
 
836
 
 
837
    AutoCaller autoCaller (this);
 
838
    CheckComRCReturnRC (autoCaller.rc());
 
839
 
 
840
    /* start with not found */
 
841
    ComObjPtr <Machine> machine;
 
842
    MachineList machines;
 
843
    {
 
844
        /* take a copy for safe iteration outside the lock */
 
845
        AutoReaderLock alock (this);
 
846
        machines = mData.mMachines;
 
847
    }
 
848
 
 
849
    for (MachineList::iterator it = machines.begin();
 
850
         !machine && it != machines.end();
 
851
         ++ it)
 
852
    {
 
853
        AutoReaderLock machineLock (*it);
 
854
        if ((*it)->userData()->mName == aName)
 
855
            machine = *it;
 
856
    }
 
857
 
 
858
    /* this will set (*machine) to NULL if machineObj is null */
 
859
    machine.queryInterfaceTo (aMachine);
 
860
 
 
861
    HRESULT rc = machine
 
862
        ? S_OK
 
863
        : setError (E_INVALIDARG,
 
864
            tr ("Could not find a registered machine named '%ls'"), aName);
 
865
 
 
866
    LogFlowThisFunc (("rc=%08X\n", rc));
 
867
    LogFlowThisFuncLeave();
 
868
 
 
869
    return rc;
 
870
}
 
871
 
 
872
/** @note Locks objects! */
 
873
STDMETHODIMP VirtualBox::UnregisterMachine (INPTR GUIDPARAM aId,
 
874
                                            IMachine **aMachine)
 
875
{
 
876
    Guid id = aId;
 
877
    if (id.isEmpty())
 
878
        return E_INVALIDARG;
 
879
 
 
880
    AutoCaller autoCaller (this);
 
881
    CheckComRCReturnRC (autoCaller.rc());
 
882
 
 
883
    AutoLock alock (this);
 
884
 
 
885
    ComObjPtr <Machine> machine;
 
886
 
 
887
    HRESULT rc = findMachine (id, true /* setError */, &machine);
 
888
    CheckComRCReturnRC (rc);
 
889
 
 
890
    rc = machine->trySetRegistered (FALSE);
 
891
    CheckComRCReturnRC (rc);
 
892
 
 
893
    /* remove from the collection of registered machines */
 
894
    mData.mMachines.remove (machine);
 
895
 
 
896
    /* save the global registry */
 
897
    rc = saveConfig();
 
898
 
 
899
    /* return the unregistered machine to the caller */
 
900
    machine.queryInterfaceTo (aMachine);
 
901
 
 
902
    /* fire an event */
 
903
    onMachineRegistered (id, FALSE);
 
904
 
 
905
    return rc;
 
906
}
 
907
 
 
908
STDMETHODIMP VirtualBox::CreateHardDisk (HardDiskStorageType_T aStorageType,
 
909
                                         IHardDisk **aHardDisk)
 
910
{
 
911
    if (!aHardDisk)
 
912
        return E_POINTER;
 
913
 
 
914
    AutoCaller autoCaller (this);
 
915
    CheckComRCReturnRC (autoCaller.rc());
 
916
 
 
917
    HRESULT rc = E_FAIL;
 
918
 
 
919
    ComObjPtr <HardDisk> hardDisk;
 
920
 
 
921
    switch (aStorageType)
 
922
    {
 
923
        case HardDiskStorageType_VirtualDiskImage:
 
924
        {
 
925
            ComObjPtr <HVirtualDiskImage> vdi;
 
926
            vdi.createObject();
 
927
            rc = vdi->init (this, NULL, NULL);
 
928
            hardDisk = vdi;
 
929
            break;
 
930
        }
 
931
 
 
932
        case HardDiskStorageType_ISCSIHardDisk:
 
933
        {
 
934
            ComObjPtr <HISCSIHardDisk> iscsi;
 
935
            iscsi.createObject();
 
936
            rc = iscsi->init (this);
 
937
            hardDisk = iscsi;
 
938
            break;
 
939
        }
 
940
 
 
941
       case HardDiskStorageType_VMDKImage:
 
942
       {
 
943
            ComObjPtr <HVMDKImage> vmdk;
 
944
            vmdk.createObject();
 
945
            rc = vmdk->init (this, NULL, NULL);
 
946
            hardDisk = vmdk;
 
947
            break;
 
948
       }
 
949
       default:
 
950
           AssertFailed();
 
951
    };
 
952
 
 
953
    if (SUCCEEDED (rc))
 
954
        hardDisk.queryInterfaceTo (aHardDisk);
 
955
 
 
956
    return rc;
 
957
}
 
958
 
 
959
/** @note Locks mSystemProperties object for reading. */
 
960
STDMETHODIMP VirtualBox::OpenHardDisk (INPTR BSTR aLocation, IHardDisk **aHardDisk)
 
961
{
 
962
    /* null and empty strings are not allowed locations */
 
963
    if (!aLocation || !(*aLocation))
 
964
        return E_INVALIDARG;
 
965
 
 
966
    if (!aHardDisk)
 
967
        return E_POINTER;
 
968
 
 
969
    AutoCaller autoCaller (this);
 
970
    CheckComRCReturnRC (autoCaller.rc());
 
971
 
 
972
    /* Currently, the location is always a path. So, append the
 
973
     * default path if only a name is given. */
 
974
    Bstr location = aLocation;
 
975
    {
 
976
        Utf8Str loc = aLocation;
 
977
        if (!RTPathHavePath (loc))
 
978
        {
 
979
            AutoLock propsLock (mData.mSystemProperties);
 
980
            location = Utf8StrFmt ("%ls%c%s",
 
981
                                   mData.mSystemProperties->defaultVDIFolder().raw(),
 
982
                                   RTPATH_DELIMITER,
 
983
                                   loc.raw());
 
984
        }
 
985
    }
 
986
 
 
987
    ComObjPtr <HardDisk> hardDisk;
 
988
    HRESULT rc = HardDisk::openHardDisk (this, location, hardDisk);
 
989
    if (SUCCEEDED (rc))
 
990
        hardDisk.queryInterfaceTo (aHardDisk);
 
991
 
 
992
    return rc;
 
993
}
 
994
 
 
995
/** @note Locks mSystemProperties object for reading. */
 
996
STDMETHODIMP VirtualBox::OpenVirtualDiskImage (INPTR BSTR aFilePath,
 
997
                                               IVirtualDiskImage **aImage)
 
998
{
 
999
    /* null and empty strings are not allowed as path names here */
 
1000
    if (!aFilePath || !(*aFilePath))
 
1001
        return E_INVALIDARG;
 
1002
 
 
1003
    if (!aImage)
 
1004
        return E_POINTER;
 
1005
 
 
1006
    AutoCaller autoCaller (this);
 
1007
    CheckComRCReturnRC (autoCaller.rc());
 
1008
 
 
1009
    /* append the default path if only a name is given */
 
1010
    Bstr path = aFilePath;
 
1011
    {
 
1012
        Utf8Str fp = aFilePath;
 
1013
        if (!RTPathHavePath (fp))
 
1014
        {
 
1015
            AutoLock propsLock (mData.mSystemProperties);
 
1016
            path = Utf8StrFmt ("%ls%c%s",
 
1017
                               mData.mSystemProperties->defaultVDIFolder().raw(),
 
1018
                               RTPATH_DELIMITER,
 
1019
                               fp.raw());
 
1020
        }
 
1021
    }
 
1022
 
 
1023
    ComObjPtr <HVirtualDiskImage> vdi;
 
1024
    vdi.createObject();
 
1025
    HRESULT rc = vdi->init (this, NULL, path);
 
1026
 
 
1027
    if (SUCCEEDED (rc))
 
1028
        vdi.queryInterfaceTo (aImage);
 
1029
 
 
1030
    return rc;
 
1031
}
 
1032
 
 
1033
/** @note Locks objects! */
 
1034
STDMETHODIMP VirtualBox::RegisterHardDisk (IHardDisk *aHardDisk)
 
1035
{
 
1036
    if (!aHardDisk)
 
1037
        return E_POINTER;
 
1038
 
 
1039
    AutoCaller autoCaller (this);
 
1040
    CheckComRCReturnRC (autoCaller.rc());
 
1041
 
 
1042
    VirtualBoxBase *child = getDependentChild (aHardDisk);
 
1043
    if (!child)
 
1044
        return setError (E_FAIL, tr ("The given hard disk is not created within "
 
1045
                                     "this VirtualBox instance"));
 
1046
 
 
1047
    /*
 
1048
     *  we can safely cast child to HardDisk * here because only HardDisk
 
1049
     *  implementations of IHardDisk can be among our children
 
1050
     */
 
1051
 
 
1052
    return registerHardDisk (static_cast <HardDisk *> (child), RHD_External);
 
1053
}
 
1054
 
 
1055
/** @note Locks objects! */
 
1056
STDMETHODIMP VirtualBox::GetHardDisk (INPTR GUIDPARAM aId, IHardDisk **aHardDisk)
 
1057
{
 
1058
    if (!aHardDisk)
 
1059
        return E_POINTER;
 
1060
 
 
1061
    AutoCaller autoCaller (this);
 
1062
    CheckComRCReturnRC (autoCaller.rc());
 
1063
 
 
1064
    Guid id = aId;
 
1065
    ComObjPtr <HardDisk> hd;
 
1066
    HRESULT rc = findHardDisk (&id, NULL, true /* setError */, &hd);
 
1067
 
 
1068
    /* the below will set *aHardDisk to NULL if hd is null */
 
1069
    hd.queryInterfaceTo (aHardDisk);
 
1070
 
 
1071
    return rc;
 
1072
}
 
1073
 
 
1074
/** @note Locks objects! */
 
1075
STDMETHODIMP VirtualBox::FindHardDisk (INPTR BSTR aLocation,
 
1076
                                       IHardDisk **aHardDisk)
 
1077
{
 
1078
   if (!aLocation)
 
1079
        return E_INVALIDARG;
 
1080
   if (!aHardDisk)
 
1081
        return E_POINTER;
 
1082
 
 
1083
    AutoCaller autoCaller (this);
 
1084
    CheckComRCReturnRC (autoCaller.rc());
 
1085
 
 
1086
    Utf8Str location = aLocation;
 
1087
    if (strncmp (location, "iscsi:", 6) == 0)
 
1088
    {
 
1089
        /* nothing special */
 
1090
    }
 
1091
    else
 
1092
    {
 
1093
        /* For locations represented by file paths, append the default path if
 
1094
         * only a name is given, and then get the full path. */
 
1095
        if (!RTPathHavePath (location))
 
1096
        {
 
1097
            AutoLock propsLock (mData.mSystemProperties);
 
1098
            location = Utf8StrFmt ("%ls%c%s",
 
1099
                                   mData.mSystemProperties->defaultVDIFolder().raw(),
 
1100
                                   RTPATH_DELIMITER,
 
1101
                                   location.raw());
 
1102
        }
 
1103
 
 
1104
        /* get the full file name */
 
1105
        char buf [RTPATH_MAX];
 
1106
        int vrc = RTPathAbsEx (mData.mHomeDir, location, buf, sizeof (buf));
 
1107
        if (VBOX_FAILURE (vrc))
 
1108
            return setError (E_FAIL, tr ("Invalid hard disk location '%ls' (%Vrc)"),
 
1109
                             aLocation, vrc);
 
1110
        location = buf;
 
1111
    }
 
1112
 
 
1113
    ComObjPtr <HardDisk> hardDisk;
 
1114
    HRESULT rc = findHardDisk (NULL, Bstr (location), true /* setError */,
 
1115
                               &hardDisk);
 
1116
 
 
1117
    /* the below will set *aHardDisk to NULL if hardDisk is null */
 
1118
    hardDisk.queryInterfaceTo (aHardDisk);
 
1119
 
 
1120
    return rc;
 
1121
}
 
1122
 
 
1123
/** @note Locks objects! */
 
1124
STDMETHODIMP VirtualBox::FindVirtualDiskImage (INPTR BSTR aFilePath,
 
1125
                                               IVirtualDiskImage **aImage)
 
1126
{
 
1127
   if (!aFilePath)
 
1128
        return E_INVALIDARG;
 
1129
   if (!aImage)
 
1130
        return E_POINTER;
 
1131
 
 
1132
    AutoCaller autoCaller (this);
 
1133
    CheckComRCReturnRC (autoCaller.rc());
 
1134
 
 
1135
    /* append the default path if only a name is given */
 
1136
    Utf8Str path = aFilePath;
 
1137
    {
 
1138
        Utf8Str fp = path;
 
1139
        if (!RTPathHavePath (fp))
 
1140
        {
 
1141
            AutoLock propsLock (mData.mSystemProperties);
 
1142
            path = Utf8StrFmt ("%ls%c%s",
 
1143
                               mData.mSystemProperties->defaultVDIFolder().raw(),
 
1144
                               RTPATH_DELIMITER,
 
1145
                               fp.raw());
 
1146
        }
 
1147
    }
 
1148
 
 
1149
    /* get the full file name */
 
1150
    char buf [RTPATH_MAX];
 
1151
    int vrc = RTPathAbsEx (mData.mHomeDir, path, buf, sizeof (buf));
 
1152
    if (VBOX_FAILURE (vrc))
 
1153
        return setError (E_FAIL, tr ("Invalid image file path '%ls' (%Vrc)"),
 
1154
                                 aFilePath, vrc);
 
1155
 
 
1156
    ComObjPtr <HVirtualDiskImage> vdi;
 
1157
    HRESULT rc = findVirtualDiskImage (NULL, Bstr (buf), true /* setError */,
 
1158
                                       &vdi);
 
1159
 
 
1160
    /* the below will set *aImage to NULL if vdi is null */
 
1161
    vdi.queryInterfaceTo (aImage);
 
1162
 
 
1163
    return rc;
 
1164
}
 
1165
 
 
1166
/** @note Locks objects! */
 
1167
STDMETHODIMP VirtualBox::UnregisterHardDisk (INPTR GUIDPARAM aId, IHardDisk **aHardDisk)
 
1168
{
 
1169
    if (!aHardDisk)
 
1170
        return E_POINTER;
 
1171
 
 
1172
    AutoCaller autoCaller (this);
 
1173
    CheckComRCReturnRC (autoCaller.rc());
 
1174
 
 
1175
    *aHardDisk = NULL;
 
1176
 
 
1177
    Guid id = aId;
 
1178
    ComObjPtr <HardDisk> hd;
 
1179
    HRESULT rc = findHardDisk (&id, NULL, true /* setError */, &hd);
 
1180
    CheckComRCReturnRC (rc);
 
1181
 
 
1182
    rc = unregisterHardDisk (hd);
 
1183
    if (SUCCEEDED (rc))
 
1184
        hd.queryInterfaceTo (aHardDisk);
 
1185
 
 
1186
    return rc;
 
1187
}
 
1188
 
 
1189
/** @note Doesn't lock anything. */
 
1190
STDMETHODIMP VirtualBox::OpenDVDImage (INPTR BSTR aFilePath, INPTR GUIDPARAM aId,
 
1191
                                       IDVDImage **aDVDImage)
 
1192
{
 
1193
    /* null and empty strings are not allowed as path names */
 
1194
    if (!aFilePath || !(*aFilePath))
 
1195
        return E_INVALIDARG;
 
1196
 
 
1197
    if (!aDVDImage)
 
1198
        return E_POINTER;
 
1199
 
 
1200
    AutoCaller autoCaller (this);
 
1201
    CheckComRCReturnRC (autoCaller.rc());
 
1202
 
 
1203
    HRESULT rc = E_FAIL;
 
1204
 
 
1205
    Guid uuid = aId;
 
1206
    /* generate an UUID if not specified */
 
1207
    if (uuid.isEmpty())
 
1208
        uuid.create();
 
1209
 
 
1210
    ComObjPtr <DVDImage> dvdImage;
 
1211
    dvdImage.createObject();
 
1212
    rc = dvdImage->init (this, aFilePath, FALSE /* !isRegistered */, uuid);
 
1213
    if (SUCCEEDED (rc))
 
1214
        dvdImage.queryInterfaceTo (aDVDImage);
 
1215
 
 
1216
    return rc;
 
1217
}
 
1218
 
 
1219
/** @note Locks objects! */
 
1220
STDMETHODIMP VirtualBox::RegisterDVDImage (IDVDImage *aDVDImage)
 
1221
{
 
1222
    if (!aDVDImage)
 
1223
        return E_POINTER;
 
1224
 
 
1225
    AutoCaller autoCaller (this);
 
1226
    CheckComRCReturnRC (autoCaller.rc());
 
1227
 
 
1228
    VirtualBoxBase *child = getDependentChild (aDVDImage);
 
1229
    if (!child)
 
1230
        return setError (E_FAIL, tr ("The given CD/DVD image is not created within "
 
1231
                                     "this VirtualBox instance"));
 
1232
 
 
1233
    /*
 
1234
     *  we can safely cast child to DVDImage * here because only DVDImage
 
1235
     *  implementations of IDVDImage can be among our children
 
1236
     */
 
1237
 
 
1238
    return registerDVDImage (static_cast <DVDImage *> (child),
 
1239
                             FALSE /* aOnStartUp */);
 
1240
}
 
1241
 
 
1242
/** @note Locks objects! */
 
1243
STDMETHODIMP VirtualBox::GetDVDImage (INPTR GUIDPARAM aId, IDVDImage **aDVDImage)
 
1244
{
 
1245
    if (!aDVDImage)
 
1246
        return E_POINTER;
 
1247
 
 
1248
    AutoCaller autoCaller (this);
 
1249
    CheckComRCReturnRC (autoCaller.rc());
 
1250
 
 
1251
    Guid uuid = aId;
 
1252
    ComObjPtr <DVDImage> dvd;
 
1253
    HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, &dvd);
 
1254
 
 
1255
    /* the below will set *aDVDImage to NULL if dvd is null */
 
1256
    dvd.queryInterfaceTo (aDVDImage);
 
1257
 
 
1258
    return rc;
 
1259
}
 
1260
 
 
1261
/** @note Locks objects! */
 
1262
STDMETHODIMP VirtualBox::FindDVDImage (INPTR BSTR aFilePath, IDVDImage **aDVDImage)
 
1263
{
 
1264
   if (!aFilePath)
 
1265
        return E_INVALIDARG;
 
1266
   if (!aDVDImage)
 
1267
        return E_POINTER;
 
1268
 
 
1269
    AutoCaller autoCaller (this);
 
1270
    CheckComRCReturnRC (autoCaller.rc());
 
1271
 
 
1272
    /* get the full file name */
 
1273
    char buf [RTPATH_MAX];
 
1274
    int vrc = RTPathAbsEx (mData.mHomeDir, Utf8Str (aFilePath), buf, sizeof (buf));
 
1275
    if (VBOX_FAILURE (vrc))
 
1276
        return setError (E_FAIL, tr ("Invalid image file path: '%ls' (%Vrc)"),
 
1277
                                 aFilePath, vrc);
 
1278
 
 
1279
    ComObjPtr <DVDImage> dvd;
 
1280
    HRESULT rc = findDVDImage (NULL, Bstr (buf), true /* setError */, &dvd);
 
1281
 
 
1282
    /* the below will set *dvdImage to NULL if dvd is null */
 
1283
    dvd.queryInterfaceTo (aDVDImage);
 
1284
 
 
1285
    return rc;
 
1286
}
 
1287
 
 
1288
/** @note Locks objects! */
 
1289
STDMETHODIMP VirtualBox::GetDVDImageUsage (INPTR GUIDPARAM aId,
 
1290
                                           ResourceUsage_T aUsage,
 
1291
                                           BSTR *aMachineIDs)
 
1292
{
 
1293
    if (!aMachineIDs)
 
1294
        return E_POINTER;
 
1295
    if (aUsage == ResourceUsage_InvalidUsage)
 
1296
        return E_INVALIDARG;
 
1297
 
 
1298
    AutoCaller autoCaller (this);
 
1299
    CheckComRCReturnRC (autoCaller.rc());
 
1300
 
 
1301
    AutoReaderLock alock (this);
 
1302
 
 
1303
    Guid uuid = Guid (aId);
 
1304
    HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, NULL);
 
1305
    if (FAILED (rc))
 
1306
        return rc;
 
1307
 
 
1308
    Bstr ids;
 
1309
    getDVDImageUsage (uuid, aUsage, &ids);
 
1310
    ids.cloneTo (aMachineIDs);
 
1311
 
 
1312
    return S_OK;
 
1313
}
 
1314
 
 
1315
/** @note Locks objects! */
 
1316
STDMETHODIMP VirtualBox::UnregisterDVDImage (INPTR GUIDPARAM aId,
 
1317
                                             IDVDImage **aDVDImage)
 
1318
{
 
1319
    if (!aDVDImage)
 
1320
        return E_POINTER;
 
1321
 
 
1322
    AutoCaller autoCaller (this);
 
1323
    CheckComRCReturnRC (autoCaller.rc());
 
1324
 
 
1325
    AutoLock alock (this);
 
1326
 
 
1327
    *aDVDImage = NULL;
 
1328
 
 
1329
    Guid uuid = aId;
 
1330
    ComObjPtr <DVDImage> dvd;
 
1331
    HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, &dvd);
 
1332
    CheckComRCReturnRC (rc);
 
1333
 
 
1334
    if (!getDVDImageUsage (aId, ResourceUsage_AllUsage))
 
1335
    {
 
1336
        /* remove from the collection */
 
1337
        mData.mDVDImages.remove (dvd);
 
1338
 
 
1339
        /* save the global config file */
 
1340
        rc = saveConfig();
 
1341
 
 
1342
        if (SUCCEEDED (rc))
 
1343
        {
 
1344
            rc = dvd.queryInterfaceTo (aDVDImage);
 
1345
            ComAssertComRC (rc);
 
1346
        }
 
1347
    }
 
1348
    else
 
1349
        rc = setError(E_FAIL,
 
1350
            tr ("The CD/DVD image with the UUID {%s} is currently in use"),
 
1351
            uuid.toString().raw());
 
1352
 
 
1353
    return rc;
 
1354
}
 
1355
 
 
1356
/** @note Doesn't lock anything. */
 
1357
STDMETHODIMP VirtualBox::OpenFloppyImage (INPTR BSTR aFilePath, INPTR GUIDPARAM aId,
 
1358
                                          IFloppyImage **aFloppyImage)
 
1359
{
 
1360
    /* null and empty strings are not allowed as path names */
 
1361
    if (!aFilePath || !(*aFilePath))
 
1362
        return E_INVALIDARG;
 
1363
 
 
1364
    if (!aFloppyImage)
 
1365
        return E_POINTER;
 
1366
 
 
1367
    AutoCaller autoCaller (this);
 
1368
    CheckComRCReturnRC (autoCaller.rc());
 
1369
 
 
1370
    HRESULT rc = E_FAIL;
 
1371
 
 
1372
    Guid uuid = aId;
 
1373
    /* generate an UUID if not specified */
 
1374
    if (Guid::isEmpty (aId))
 
1375
        uuid.create();
 
1376
 
 
1377
    ComObjPtr <FloppyImage> floppyImage;
 
1378
    floppyImage.createObject();
 
1379
    rc = floppyImage->init (this, aFilePath, FALSE /* !isRegistered */, uuid);
 
1380
    if (SUCCEEDED (rc))
 
1381
        floppyImage.queryInterfaceTo (aFloppyImage);
 
1382
 
 
1383
    return rc;
 
1384
}
 
1385
 
 
1386
/** @note Locks objects! */
 
1387
STDMETHODIMP VirtualBox::RegisterFloppyImage (IFloppyImage *aFloppyImage)
 
1388
{
 
1389
    if (!aFloppyImage)
 
1390
        return E_POINTER;
 
1391
 
 
1392
    AutoCaller autoCaller (this);
 
1393
    CheckComRCReturnRC (autoCaller.rc());
 
1394
 
 
1395
    VirtualBoxBase *child = getDependentChild (aFloppyImage);
 
1396
    if (!child)
 
1397
        return setError (E_FAIL, tr ("The given floppy image is not created within "
 
1398
                                     "this VirtualBox instance"));
 
1399
 
 
1400
    /*
 
1401
     *  we can safely cast child to FloppyImage * here because only FloppyImage
 
1402
     *  implementations of IFloppyImage can be among our children
 
1403
     */
 
1404
 
 
1405
    return registerFloppyImage (static_cast <FloppyImage *> (child),
 
1406
                                FALSE /* aOnStartUp */);
 
1407
}
 
1408
 
 
1409
/** @note Locks objects! */
 
1410
STDMETHODIMP VirtualBox::GetFloppyImage (INPTR GUIDPARAM aId,
 
1411
                                         IFloppyImage **aFloppyImage)
 
1412
{
 
1413
    if (!aFloppyImage)
 
1414
        return E_POINTER;
 
1415
 
 
1416
    AutoCaller autoCaller (this);
 
1417
    CheckComRCReturnRC (autoCaller.rc());
 
1418
 
 
1419
    Guid uuid = aId;
 
1420
    ComObjPtr <FloppyImage> floppy;
 
1421
    HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, &floppy);
 
1422
 
 
1423
    /* the below will set *aFloppyImage to NULL if dvd is null */
 
1424
    floppy.queryInterfaceTo (aFloppyImage);
 
1425
 
 
1426
    return rc;
 
1427
}
 
1428
 
 
1429
/** @note Locks objects! */
 
1430
STDMETHODIMP VirtualBox::FindFloppyImage (INPTR BSTR aFilePath,
 
1431
                                          IFloppyImage **aFloppyImage)
 
1432
{
 
1433
   if (!aFilePath)
 
1434
        return E_INVALIDARG;
 
1435
   if (!aFloppyImage)
 
1436
        return E_POINTER;
 
1437
 
 
1438
    AutoCaller autoCaller (this);
 
1439
    CheckComRCReturnRC (autoCaller.rc());
 
1440
 
 
1441
    /* get the full file name */
 
1442
    char buf [RTPATH_MAX];
 
1443
    int vrc = RTPathAbsEx (mData.mHomeDir, Utf8Str (aFilePath), buf, sizeof (buf));
 
1444
    if (VBOX_FAILURE (vrc))
 
1445
        return setError (E_FAIL, tr ("Invalid image file path: '%ls' (%Vrc)"),
 
1446
                                 aFilePath, vrc);
 
1447
 
 
1448
    ComObjPtr <FloppyImage> floppy;
 
1449
    HRESULT rc = findFloppyImage (NULL, Bstr (buf), true /* setError */, &floppy);
 
1450
 
 
1451
    /* the below will set *image to NULL if img is null */
 
1452
    floppy.queryInterfaceTo (aFloppyImage);
 
1453
 
 
1454
    return rc;
 
1455
}
 
1456
 
 
1457
/** @note Locks objects! */
 
1458
STDMETHODIMP VirtualBox::GetFloppyImageUsage (INPTR GUIDPARAM aId,
 
1459
                                              ResourceUsage_T aUsage,
 
1460
                                              BSTR *aMachineIDs)
 
1461
{
 
1462
    if (!aMachineIDs)
 
1463
        return E_POINTER;
 
1464
    if (aUsage == ResourceUsage_InvalidUsage)
 
1465
        return E_INVALIDARG;
 
1466
 
 
1467
    AutoCaller autoCaller (this);
 
1468
    CheckComRCReturnRC (autoCaller.rc());
 
1469
 
 
1470
    AutoReaderLock alock (this);
 
1471
 
 
1472
    Guid uuid = Guid (aId);
 
1473
    HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, NULL);
 
1474
    if (FAILED (rc))
 
1475
        return rc;
 
1476
 
 
1477
    Bstr ids;
 
1478
    getFloppyImageUsage (uuid, aUsage, &ids);
 
1479
    ids.cloneTo (aMachineIDs);
 
1480
 
 
1481
    return S_OK;
 
1482
}
 
1483
 
 
1484
/** @note Locks objects! */
 
1485
STDMETHODIMP VirtualBox::UnregisterFloppyImage (INPTR GUIDPARAM aId,
 
1486
                                                IFloppyImage **aFloppyImage)
 
1487
{
 
1488
    if (!aFloppyImage)
 
1489
        return E_INVALIDARG;
 
1490
 
 
1491
    AutoCaller autoCaller (this);
 
1492
    CheckComRCReturnRC (autoCaller.rc());
 
1493
 
 
1494
    AutoLock alock (this);
 
1495
 
 
1496
    *aFloppyImage = NULL;
 
1497
 
 
1498
    Guid uuid = aId;
 
1499
    ComObjPtr <FloppyImage> floppy;
 
1500
    HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, &floppy);
 
1501
    CheckComRCReturnRC (rc);
 
1502
 
 
1503
    if (!getFloppyImageUsage (aId, ResourceUsage_AllUsage))
 
1504
    {
 
1505
        /* remove from the collection */
 
1506
        mData.mFloppyImages.remove (floppy);
 
1507
 
 
1508
        /* save the global config file */
 
1509
        rc = saveConfig();
 
1510
        if (SUCCEEDED (rc))
 
1511
        {
 
1512
            rc = floppy.queryInterfaceTo (aFloppyImage);
 
1513
            ComAssertComRC (rc);
 
1514
        }
 
1515
    }
 
1516
    else
 
1517
        rc = setError(E_FAIL,
 
1518
            tr ("A floppy image with UUID {%s} is currently in use"),
 
1519
            uuid.toString().raw());
 
1520
 
 
1521
    return rc;
 
1522
}
 
1523
 
 
1524
/** @note Locks this object for reading. */
 
1525
STDMETHODIMP VirtualBox::GetGuestOSType (INPTR BSTR aId, IGuestOSType **aType)
 
1526
{
 
1527
    if (!aType)
 
1528
        return E_INVALIDARG;
 
1529
 
 
1530
    AutoCaller autoCaller (this);
 
1531
    CheckComRCReturnRC (autoCaller.rc());
 
1532
 
 
1533
    *aType = NULL;
 
1534
 
 
1535
    AutoReaderLock alock (this);
 
1536
 
 
1537
    for (GuestOSTypeList::iterator it = mData.mGuestOSTypes.begin();
 
1538
         it != mData.mGuestOSTypes.end();
 
1539
         ++ it)
 
1540
    {
 
1541
        const Bstr &typeId = (*it)->id();
 
1542
        AssertMsg (!!typeId, ("ID must not be NULL"));
 
1543
        if (typeId == aId)
 
1544
        {
 
1545
            (*it).queryInterfaceTo (aType);
 
1546
            break;
 
1547
        }
 
1548
    }
 
1549
 
 
1550
    return (*aType) ? S_OK :
 
1551
        setError (E_INVALIDARG,
 
1552
            tr ("'%ls' is not a valid Guest OS type"),
 
1553
            aId);
 
1554
}
 
1555
 
 
1556
STDMETHODIMP
 
1557
VirtualBox::CreateSharedFolder (INPTR BSTR aName, INPTR BSTR aHostPath)
 
1558
{
 
1559
    if (!aName || !aHostPath)
 
1560
        return E_INVALIDARG;
 
1561
 
 
1562
    AutoCaller autoCaller (this);
 
1563
    CheckComRCReturnRC (autoCaller.rc());
 
1564
 
 
1565
    return setError (E_NOTIMPL, "Not yet implemented");
 
1566
}
 
1567
 
 
1568
STDMETHODIMP VirtualBox::RemoveSharedFolder (INPTR BSTR aName)
 
1569
{
 
1570
    if (!aName)
 
1571
        return E_INVALIDARG;
 
1572
 
 
1573
    AutoCaller autoCaller (this);
 
1574
    CheckComRCReturnRC (autoCaller.rc());
 
1575
 
 
1576
    return setError (E_NOTIMPL, "Not yet implemented");
 
1577
}
 
1578
 
 
1579
/** @note Locks this object for reading. */
 
1580
STDMETHODIMP VirtualBox::
 
1581
GetNextExtraDataKey (INPTR BSTR aKey, BSTR *aNextKey, BSTR *aNextValue)
 
1582
{
 
1583
    if (!aNextKey)
 
1584
        return E_POINTER;
 
1585
 
 
1586
    AutoCaller autoCaller (this);
 
1587
    CheckComRCReturnRC (autoCaller.rc());
 
1588
 
 
1589
    /* start with nothing found */
 
1590
    *aNextKey = NULL;
 
1591
 
 
1592
    HRESULT rc = S_OK;
 
1593
 
 
1594
    /* serialize file access */
 
1595
    AutoReaderLock alock (this);
 
1596
 
 
1597
    CFGHANDLE configLoader;
 
1598
 
 
1599
    /* load the config file */
 
1600
    int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
 
1601
                          mData.mCfgFile.mHandle,
 
1602
                          XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
 
1603
    ComAssertRCRet (vrc, E_FAIL);
 
1604
 
 
1605
    CFGNODE extraDataNode;
 
1606
 
 
1607
    /* navigate to the right position */
 
1608
    if (VBOX_SUCCESS (CFGLDRGetNode (configLoader,
 
1609
                                     "VirtualBox/Global/ExtraData", 0,
 
1610
                                     &extraDataNode)))
 
1611
    {
 
1612
        /* check if it exists */
 
1613
        bool found = false;
 
1614
        unsigned count;
 
1615
        CFGNODE extraDataItemNode;
 
1616
        CFGLDRCountChildren (extraDataNode, "ExtraDataItem", &count);
 
1617
        for (unsigned i = 0; (i < count) && (found == false); i++)
 
1618
        {
 
1619
            Bstr name;
 
1620
            CFGLDRGetChildNode (extraDataNode, "ExtraDataItem", i, &extraDataItemNode);
 
1621
            CFGLDRQueryBSTR (extraDataItemNode, "name", name.asOutParam());
 
1622
 
 
1623
            /* if we're supposed to return the first one */
 
1624
            if (aKey == NULL)
 
1625
            {
 
1626
                name.cloneTo (aNextKey);
 
1627
                if (aNextValue)
 
1628
                    CFGLDRQueryBSTR (extraDataItemNode, "value", aNextValue);
 
1629
                found = true;
 
1630
            }
 
1631
            /* did we find the key we're looking for? */
 
1632
            else if (name == aKey)
 
1633
            {
 
1634
                found = true;
 
1635
                /* is there another item? */
 
1636
                if (i + 1 < count)
 
1637
                {
 
1638
                    CFGLDRGetChildNode (extraDataNode, "ExtraDataItem", i + 1,
 
1639
                                        &extraDataItemNode);
 
1640
                    CFGLDRQueryBSTR (extraDataItemNode, "name", name.asOutParam());
 
1641
                    name.cloneTo (aNextKey);
 
1642
                    if (aNextValue)
 
1643
                        CFGLDRQueryBSTR (extraDataItemNode, "value", aNextValue);
 
1644
                    found = true;
 
1645
                }
 
1646
                else
 
1647
                {
 
1648
                    /* it's the last one */
 
1649
                    *aNextKey = NULL;
 
1650
                }
 
1651
            }
 
1652
            CFGLDRReleaseNode (extraDataItemNode);
 
1653
        }
 
1654
 
 
1655
        /* if we haven't found the key, it's an error */
 
1656
        if (!found)
 
1657
            rc = setError (E_FAIL,
 
1658
                tr ("Could not find extra data key '%ls'"), aKey);
 
1659
 
 
1660
        CFGLDRReleaseNode (extraDataNode);
 
1661
    }
 
1662
 
 
1663
    CFGLDRFree (configLoader);
 
1664
 
 
1665
    return rc;
 
1666
}
 
1667
 
 
1668
/** @note Locks this object for reading. */
 
1669
STDMETHODIMP VirtualBox::GetExtraData (INPTR BSTR aKey, BSTR *aValue)
 
1670
{
 
1671
    if (!aKey || !(*aKey))
 
1672
        return E_INVALIDARG;
 
1673
    if (!aValue)
 
1674
        return E_POINTER;
 
1675
 
 
1676
    AutoCaller autoCaller (this);
 
1677
    CheckComRCReturnRC (autoCaller.rc());
 
1678
 
 
1679
    /* start with nothing found */
 
1680
    *aValue = NULL;
 
1681
 
 
1682
    HRESULT rc = S_OK;
 
1683
 
 
1684
    /* serialize file access */
 
1685
    AutoReaderLock alock (this);
 
1686
 
 
1687
    CFGHANDLE configLoader;
 
1688
 
 
1689
    /* load the config file */
 
1690
    int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
 
1691
                          mData.mCfgFile.mHandle,
 
1692
                          XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
 
1693
    ComAssertRCRet (vrc, E_FAIL);
 
1694
 
 
1695
    CFGNODE extraDataNode;
 
1696
 
 
1697
    /* navigate to the right position */
 
1698
    if (VBOX_SUCCESS (CFGLDRGetNode (configLoader,
 
1699
                                     "VirtualBox/Global/ExtraData", 0,
 
1700
                                     &extraDataNode)))
 
1701
    {
 
1702
        /* check if it exists */
 
1703
        bool found = false;
 
1704
        unsigned count;
 
1705
        CFGNODE extraDataItemNode;
 
1706
        CFGLDRCountChildren (extraDataNode, "ExtraDataItem", &count);
 
1707
        for (unsigned i = 0; (i < count) && (found == false); i++)
 
1708
        {
 
1709
            Bstr name;
 
1710
            CFGLDRGetChildNode (extraDataNode, "ExtraDataItem", i, &extraDataItemNode);
 
1711
            CFGLDRQueryBSTR (extraDataItemNode, "name", name.asOutParam());
 
1712
            if (name == aKey)
 
1713
            {
 
1714
                found = true;
 
1715
                CFGLDRQueryBSTR (extraDataItemNode, "value", aValue);
 
1716
            }
 
1717
            CFGLDRReleaseNode (extraDataItemNode);
 
1718
        }
 
1719
 
 
1720
        CFGLDRReleaseNode (extraDataNode);
 
1721
    }
 
1722
 
 
1723
    CFGLDRFree (configLoader);
 
1724
 
 
1725
    return rc;
 
1726
}
 
1727
 
 
1728
/** @note Locks this object for writing. */
 
1729
STDMETHODIMP VirtualBox::SetExtraData(INPTR BSTR aKey, INPTR BSTR aValue)
 
1730
{
 
1731
    if (!aKey)
 
1732
        return E_POINTER;
 
1733
 
 
1734
    AutoCaller autoCaller (this);
 
1735
    CheckComRCReturnRC (autoCaller.rc());
 
1736
 
 
1737
    Guid emptyGuid;
 
1738
 
 
1739
    bool changed = false;
 
1740
    HRESULT rc = S_OK;
 
1741
 
 
1742
    /* serialize file access */
 
1743
    AutoLock alock (this);
 
1744
 
 
1745
    CFGHANDLE configLoader;
 
1746
 
 
1747
    /* load the config file */
 
1748
    int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
 
1749
                          mData.mCfgFile.mHandle,
 
1750
                          XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
 
1751
    ComAssertRCRet (vrc, E_FAIL);
 
1752
 
 
1753
    CFGNODE extraDataNode = 0;
 
1754
 
 
1755
    vrc = CFGLDRGetNode (configLoader, "VirtualBox/Global/ExtraData", 0,
 
1756
                         &extraDataNode);
 
1757
    if (VBOX_FAILURE (vrc) && aValue)
 
1758
        vrc = CFGLDRCreateNode (configLoader, "VirtualBox/Global/ExtraData",
 
1759
                                &extraDataNode);
 
1760
 
 
1761
    if (extraDataNode)
 
1762
    {
 
1763
        CFGNODE extraDataItemNode = 0;
 
1764
        Bstr oldVal;
 
1765
 
 
1766
        unsigned count;
 
1767
        CFGLDRCountChildren (extraDataNode, "ExtraDataItem", &count);
 
1768
 
 
1769
        for (unsigned i = 0; i < count; i++)
 
1770
        {
 
1771
            CFGLDRGetChildNode (extraDataNode, "ExtraDataItem", i, &extraDataItemNode);
 
1772
            Bstr name;
 
1773
            CFGLDRQueryBSTR (extraDataItemNode, "name", name.asOutParam());
 
1774
            if (name == aKey)
 
1775
            {
 
1776
                CFGLDRQueryBSTR (extraDataItemNode, "value", oldVal.asOutParam());
 
1777
                break;
 
1778
            }
 
1779
            CFGLDRReleaseNode (extraDataItemNode);
 
1780
            extraDataItemNode = 0;
 
1781
        }
 
1782
 
 
1783
        /*
 
1784
         *  When no key is found, oldVal is null. Note:
 
1785
         *  1. when oldVal is null, |oldVal == (BSTR) NULL| is true
 
1786
         *  2. we cannot do |oldVal != value| because it will compare
 
1787
         *  BSTR pointers instead of strings (due to type conversion ops)
 
1788
         */
 
1789
        changed = !(oldVal == aValue);
 
1790
 
 
1791
        if (changed)
 
1792
        {
 
1793
            /* ask for permission from all listeners */
 
1794
            Bstr error;
 
1795
            if (!onExtraDataCanChange (emptyGuid, aKey, aValue, error))
 
1796
            {
 
1797
                const char *sep = error.isEmpty() ? "" : ": ";
 
1798
                const BSTR err = error.isNull() ? (const BSTR) L"" : error.raw();
 
1799
                LogWarningFunc (("Someone vetoed! Change refused%s%ls\n",
 
1800
                                 sep, err));
 
1801
                rc = setError (E_ACCESSDENIED,
 
1802
                    tr ("Could not set extra data because someone refused "
 
1803
                        "the requested change of '%ls' to '%ls'%s%ls"),
 
1804
                    aKey, aValue, sep, err);
 
1805
            }
 
1806
            else
 
1807
            {
 
1808
                if (aValue)
 
1809
                {
 
1810
                    if (!extraDataItemNode)
 
1811
                    {
 
1812
                        /* create a new item */
 
1813
                        CFGLDRAppendChildNode (extraDataNode, "ExtraDataItem",
 
1814
                                               &extraDataItemNode);
 
1815
                        CFGLDRSetBSTR (extraDataItemNode, "name", aKey);
 
1816
                    }
 
1817
                    CFGLDRSetBSTR (extraDataItemNode, "value", aValue);
 
1818
                }
 
1819
                else
 
1820
                {
 
1821
                    /* an old value does for sure exist here */
 
1822
                    CFGLDRDeleteNode (extraDataItemNode);
 
1823
                    extraDataItemNode = 0;
 
1824
                }
 
1825
            }
 
1826
        }
 
1827
 
 
1828
        if (extraDataItemNode)
 
1829
            CFGLDRReleaseNode (extraDataItemNode);
 
1830
 
 
1831
        CFGLDRReleaseNode (extraDataNode);
 
1832
 
 
1833
        if (SUCCEEDED (rc) && changed)
 
1834
        {
 
1835
            char *loaderError = NULL;
 
1836
            vrc = CFGLDRSave (configLoader, &loaderError);
 
1837
            if (VBOX_FAILURE (vrc))
 
1838
            {
 
1839
                rc = setError (E_FAIL,
 
1840
                    tr ("Could not save the settings file '%ls' (%Vrc)%s%s"),
 
1841
                    mData.mCfgFile.mName.raw(), vrc,
 
1842
                    loaderError ? ".\n" : "", loaderError ? loaderError : "");
 
1843
                if (loaderError)
 
1844
                    RTMemTmpFree (loaderError);
 
1845
            }
 
1846
        }
 
1847
    }
 
1848
 
 
1849
    CFGLDRFree (configLoader);
 
1850
 
 
1851
    /* notification handling */
 
1852
    if (SUCCEEDED (rc) && changed)
 
1853
        onExtraDataChange (emptyGuid, aKey, aValue);
 
1854
 
 
1855
    return rc;
 
1856
}
 
1857
 
 
1858
/**
 
1859
 *  @note Locks objects!
 
1860
 */
 
1861
STDMETHODIMP VirtualBox::OpenSession (ISession *aSession, INPTR GUIDPARAM aMachineId)
 
1862
{
 
1863
    if (!aSession)
 
1864
        return E_INVALIDARG;
 
1865
 
 
1866
    AutoCaller autoCaller (this);
 
1867
    CheckComRCReturnRC (autoCaller.rc());
 
1868
 
 
1869
    Guid id = aMachineId;
 
1870
    ComObjPtr <Machine> machine;
 
1871
 
 
1872
    HRESULT rc = findMachine (id, true /* setError */, &machine);
 
1873
    CheckComRCReturnRC (rc);
 
1874
 
 
1875
    /* check the session state */
 
1876
    SessionState_T state;
 
1877
    rc = aSession->COMGETTER(State) (&state);
 
1878
    CheckComRCReturnRC (rc);
 
1879
 
 
1880
    if (state != SessionState_SessionClosed)
 
1881
        return setError (E_INVALIDARG,
 
1882
            tr ("The given session is already open or being opened"));
 
1883
 
 
1884
    /* get the IInternalSessionControl interface */
 
1885
    ComPtr <IInternalSessionControl> control = aSession;
 
1886
    ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
 
1887
                     E_INVALIDARG);
 
1888
 
 
1889
    rc = machine->openSession (control);
 
1890
 
 
1891
    if (SUCCEEDED (rc))
 
1892
    {
 
1893
        /*
 
1894
         *  tell the client watcher thread to update the set of
 
1895
         *  machines that have open sessions
 
1896
         */
 
1897
        updateClientWatcher();
 
1898
 
 
1899
        /* fire an event */
 
1900
        onSessionStateChange (aMachineId, SessionState_SessionOpen);
 
1901
    }
 
1902
 
 
1903
    return rc;
 
1904
}
 
1905
 
 
1906
/**
 
1907
 *  @note Locks objects!
 
1908
 */
 
1909
STDMETHODIMP VirtualBox::OpenRemoteSession (ISession *aSession,
 
1910
                                            INPTR GUIDPARAM aMachineId,
 
1911
                                            INPTR BSTR aType,
 
1912
                                            INPTR BSTR aEnvironment,
 
1913
                                            IProgress **aProgress)
 
1914
{
 
1915
    if (!aSession || !aType)
 
1916
        return E_INVALIDARG;
 
1917
    if (!aProgress)
 
1918
        return E_POINTER;
 
1919
 
 
1920
    AutoCaller autoCaller (this);
 
1921
    CheckComRCReturnRC (autoCaller.rc());
 
1922
 
 
1923
    Guid id = aMachineId;
 
1924
    ComObjPtr <Machine> machine;
 
1925
 
 
1926
    HRESULT rc = findMachine (id, true /* setError */, &machine);
 
1927
    CheckComRCReturnRC (rc);
 
1928
 
 
1929
    /* check the session state */
 
1930
    SessionState_T state;
 
1931
    rc = aSession->COMGETTER(State) (&state);
 
1932
    CheckComRCReturnRC (rc);
 
1933
 
 
1934
    if (state != SessionState_SessionClosed)
 
1935
        return setError (E_INVALIDARG,
 
1936
            tr ("The given session is already open or being opened"));
 
1937
 
 
1938
    /* get the IInternalSessionControl interface */
 
1939
    ComPtr <IInternalSessionControl> control = aSession;
 
1940
    ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
 
1941
                     E_INVALIDARG);
 
1942
 
 
1943
    /* create a progress object */
 
1944
    ComObjPtr <Progress> progress;
 
1945
    progress.createObject();
 
1946
    progress->init (this, (IMachine *) machine,
 
1947
                    Bstr (tr ("Spawning session")),
 
1948
                    FALSE /* aCancelable */);
 
1949
 
 
1950
    rc = machine->openRemoteSession (control, aType, aEnvironment, progress);
 
1951
 
 
1952
    if (SUCCEEDED (rc))
 
1953
    {
 
1954
        progress.queryInterfaceTo (aProgress);
 
1955
 
 
1956
        /* fire an event */
 
1957
        onSessionStateChange (aMachineId, SessionState_SessionSpawning);
 
1958
    }
 
1959
 
 
1960
    return rc;
 
1961
}
 
1962
 
 
1963
/**
 
1964
 *  @note Locks objects!
 
1965
 */
 
1966
STDMETHODIMP VirtualBox::OpenExistingSession (ISession *aSession,
 
1967
                                              INPTR GUIDPARAM aMachineId)
 
1968
{
 
1969
    if (!aSession)
 
1970
        return E_POINTER;
 
1971
 
 
1972
    AutoCaller autoCaller (this);
 
1973
    CheckComRCReturnRC (autoCaller.rc());
 
1974
 
 
1975
    Guid id = aMachineId;
 
1976
    ComObjPtr <Machine> machine;
 
1977
 
 
1978
    HRESULT rc = findMachine (id, true /* setError */, &machine);
 
1979
    CheckComRCReturnRC (rc);
 
1980
 
 
1981
    /* check the session state */
 
1982
    SessionState_T state;
 
1983
    rc = aSession->COMGETTER(State) (&state);
 
1984
    CheckComRCReturnRC (rc);
 
1985
 
 
1986
    if (state != SessionState_SessionClosed)
 
1987
        return setError (E_INVALIDARG,
 
1988
            tr ("The given session is already open or being opened"));
 
1989
 
 
1990
    /* get the IInternalSessionControl interface */
 
1991
    ComPtr <IInternalSessionControl> control = aSession;
 
1992
    ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
 
1993
                     E_INVALIDARG);
 
1994
 
 
1995
    rc = machine->openExistingSession (control);
 
1996
 
 
1997
    return rc;
 
1998
}
 
1999
 
 
2000
/**
 
2001
 *  Registers a new client callback on this instance. The methods of the
 
2002
 *  callback interface will be called by this instance when the appropriate
 
2003
 *  event occurs.
 
2004
 *
 
2005
 *  @note Locks this object for writing.
 
2006
 */
 
2007
STDMETHODIMP VirtualBox::RegisterCallback (IVirtualBoxCallback *callback)
 
2008
{
 
2009
    LogFlowMember (("VirtualBox::RegisterCallback(): callback=%p\n", callback));
 
2010
 
 
2011
    if (!callback)
 
2012
        return E_INVALIDARG;
 
2013
 
 
2014
    AutoCaller autoCaller (this);
 
2015
    CheckComRCReturnRC (autoCaller.rc());
 
2016
 
 
2017
    AutoLock alock (this);
 
2018
    mData.mCallbacks.push_back (CallbackList::value_type (callback));
 
2019
 
 
2020
    return S_OK;
 
2021
}
 
2022
 
 
2023
/**
 
2024
 *  Unregisters the previously registered client callback.
 
2025
 *
 
2026
 *  @note Locks this object for writing.
 
2027
 */
 
2028
STDMETHODIMP VirtualBox::UnregisterCallback (IVirtualBoxCallback *callback)
 
2029
{
 
2030
    if (!callback)
 
2031
        return E_INVALIDARG;
 
2032
 
 
2033
    AutoCaller autoCaller (this);
 
2034
    CheckComRCReturnRC (autoCaller.rc());
 
2035
 
 
2036
    HRESULT rc = S_OK;
 
2037
 
 
2038
    AutoLock alock (this);
 
2039
 
 
2040
    CallbackList::iterator it;
 
2041
    it = std::find (mData.mCallbacks.begin(),
 
2042
                    mData.mCallbacks.end(),
 
2043
                    CallbackList::value_type (callback));
 
2044
    if (it == mData.mCallbacks.end())
 
2045
        rc = E_INVALIDARG;
 
2046
    else
 
2047
        mData.mCallbacks.erase (it);
 
2048
 
 
2049
    LogFlowMember (("VirtualBox::UnregisterCallback(): callback=%p, rc=%08X\n",
 
2050
                    callback, rc));
 
2051
    return rc;
 
2052
}
 
2053
 
 
2054
// public methods only for internal purposes
 
2055
/////////////////////////////////////////////////////////////////////////////
 
2056
 
 
2057
/**
 
2058
 *  Posts an event to the event queue that is processed asynchronously
 
2059
 *  on a dedicated thread.
 
2060
 *
 
2061
 *  Posting events to the dedicated event queue is useful to perform secondary
 
2062
 *  actions outside any object locks -- for example, to iterate over a list
 
2063
 *  of callbacks and inform them about some change caused by some object's
 
2064
 *  method call.
 
2065
 *
 
2066
 *  @param event    event to post
 
2067
 *                  (must be allocated using |new|, will be deleted automatically
 
2068
 *                  by the event thread after processing)
 
2069
 *
 
2070
 *  @note Doesn't lock any object.
 
2071
 */
 
2072
HRESULT VirtualBox::postEvent (Event *event)
 
2073
{
 
2074
    AutoCaller autoCaller (this);
 
2075
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
2076
 
 
2077
    if (autoCaller.state() != Ready)
 
2078
    {
 
2079
        LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
 
2080
                         "the event is discarded!\n",
 
2081
                         autoCaller.state()));
 
2082
        return S_OK;
 
2083
    }
 
2084
 
 
2085
    AssertReturn (event, E_FAIL);
 
2086
    AssertReturn (mAsyncEventQ, E_FAIL);
 
2087
 
 
2088
    AutoLock alock (mAsyncEventQLock);
 
2089
    if (mAsyncEventQ->postEvent (event))
 
2090
        return S_OK;
 
2091
 
 
2092
    return E_FAIL;
 
2093
}
 
2094
 
 
2095
/**
 
2096
 *  Helper method to add a progress to the global collection of pending
 
2097
 *  operations.
 
2098
 *
 
2099
 *  @param   aProgress  operation to add to the collection
 
2100
 *  @return  COM status code
 
2101
 *
 
2102
 *  @note Locks this object for writing.
 
2103
 */
 
2104
HRESULT VirtualBox::addProgress (IProgress *aProgress)
 
2105
{
 
2106
    if (!aProgress)
 
2107
        return E_INVALIDARG;
 
2108
 
 
2109
    AutoCaller autoCaller (this);
 
2110
    CheckComRCReturnRC (autoCaller.rc());
 
2111
 
 
2112
    AutoLock alock (this);
 
2113
    mData.mProgressOperations.push_back (aProgress);
 
2114
    return S_OK;
 
2115
}
 
2116
 
 
2117
/**
 
2118
 *  Helper method to remove the progress from the global collection of pending
 
2119
 *  operations. Usualy gets called upon progress completion.
 
2120
 *
 
2121
 *  @param   aId    UUID of the progress operation to remove
 
2122
 *  @return  COM status code
 
2123
 *
 
2124
 *  @note Locks this object for writing.
 
2125
 */
 
2126
HRESULT VirtualBox::removeProgress (INPTR GUIDPARAM aId)
 
2127
{
 
2128
    AutoCaller autoCaller (this);
 
2129
    CheckComRCReturnRC (autoCaller.rc());
 
2130
 
 
2131
    ComPtr <IProgress> progress;
 
2132
 
 
2133
    AutoLock alock (this);
 
2134
 
 
2135
    for (ProgressList::iterator it = mData.mProgressOperations.begin();
 
2136
         it != mData.mProgressOperations.end();
 
2137
         ++ it)
 
2138
    {
 
2139
        Guid id;
 
2140
        (*it)->COMGETTER(Id) (id.asOutParam());
 
2141
        if (id == aId)
 
2142
        {
 
2143
            mData.mProgressOperations.erase (it);
 
2144
            return S_OK;
 
2145
        }
 
2146
    }
 
2147
 
 
2148
    AssertFailed(); /* should never happen */
 
2149
 
 
2150
    return E_FAIL;
 
2151
}
 
2152
 
 
2153
#ifdef RT_OS_WINDOWS
 
2154
 
 
2155
struct StartSVCHelperClientData
 
2156
{
 
2157
    ComObjPtr <VirtualBox> that;
 
2158
    ComObjPtr <Progress> progress;
 
2159
    bool privileged;
 
2160
    VirtualBox::SVCHelperClientFunc func;
 
2161
    void *user;
 
2162
};
 
2163
 
 
2164
/**
 
2165
 *  Helper method to that starts a worker thread that:
 
2166
 *  - creates a pipe communication channel using SVCHlpClient;
 
2167
 *  - starts a SVC Helper process that will inherit this channel;
 
2168
 *  - executes the supplied function by passing it the created SVCHlpClient
 
2169
 *    and opened instance to communicate to the Helper process and the given
 
2170
 *    Progress object.
 
2171
 *
 
2172
 *  The user function is supposed to communicate to the helper process
 
2173
 *  using the \a aClient argument to do the requested job and optionally expose
 
2174
 *  the prgress through the \a aProgress object. The user function should never
 
2175
 *  call notifyComplete() on it: this will be done automatically using the
 
2176
 *  result code returned by the function.
 
2177
 *
 
2178
 *  Before the user function is stared, the communication channel passed to in
 
2179
 *  the \a aClient argument, is fully set up, the function should start using
 
2180
 *  it's write() and read() methods directly.
 
2181
 *
 
2182
 *  The \a aVrc parameter of the user function may be used to return an error
 
2183
 *  code if it is related to communication errors (for example, returned by
 
2184
 *  the SVCHlpClient members when they fail). In this case, the correct error
 
2185
 *  message using this value will be reported to the caller. Note that the
 
2186
 *  value of \a aVrc is inspected only if the user function itself returns
 
2187
 *  a success.
 
2188
 *
 
2189
 *  If a failure happens anywhere before the user function would be normally
 
2190
 *  called, it will be called anyway in special "cleanup only" mode indicated
 
2191
 *  by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
 
2192
 *  all the function is supposed to do is to cleanup its aUser argument if
 
2193
 *  necessary (it's assumed that the ownership of this argument is passed to
 
2194
 *  the user function once #startSVCHelperClient() returns a success, thus
 
2195
 *  making it responsible for the cleanup).
 
2196
 *
 
2197
 *  After the user function returns, the thread will send the SVCHlpMsg::Null
 
2198
 *  message to indicate a process termination.
 
2199
 *
 
2200
 *  @param  aPrivileged |true| to start the SVC Hepler process as a privlieged
 
2201
 *                      user that can perform administrative tasks
 
2202
 *  @param  aFunc       user function to run
 
2203
 *  @param  aUser       argument to the user function
 
2204
 *  @param  aProgress   progress object that will track operation completion
 
2205
 *
 
2206
 *  @note aPrivileged is currently ignored (due to some unsolved problems in
 
2207
 *        Vista) and the process will be started as a normal (unprivileged)
 
2208
 *        process.
 
2209
 *
 
2210
 *  @note Doesn't lock anything.
 
2211
 */
 
2212
HRESULT VirtualBox::startSVCHelperClient (bool aPrivileged,
 
2213
                                          SVCHelperClientFunc aFunc,
 
2214
                                          void *aUser, Progress *aProgress)
 
2215
{
 
2216
    AssertReturn (aFunc, E_POINTER);
 
2217
    AssertReturn (aProgress, E_POINTER);
 
2218
 
 
2219
    AutoCaller autoCaller (this);
 
2220
    CheckComRCReturnRC (autoCaller.rc());
 
2221
 
 
2222
    /* create the SVCHelperClientThread() argument */
 
2223
    std::auto_ptr <StartSVCHelperClientData>
 
2224
        d (new StartSVCHelperClientData());
 
2225
    AssertReturn (d.get(), E_OUTOFMEMORY);
 
2226
 
 
2227
    d->that = this;
 
2228
    d->progress = aProgress;
 
2229
    d->privileged = aPrivileged;
 
2230
    d->func = aFunc;
 
2231
    d->user = aUser;
 
2232
 
 
2233
    RTTHREAD tid = NIL_RTTHREAD;
 
2234
    int vrc = RTThreadCreate (&tid, SVCHelperClientThread,
 
2235
                              static_cast <void *> (d.get()),
 
2236
                              0, RTTHREADTYPE_MAIN_WORKER,
 
2237
                              RTTHREADFLAGS_WAITABLE, "SVCHelper");
 
2238
 
 
2239
    ComAssertMsgRCRet (vrc, ("Could not create SVCHelper thread (%Vrc)\n", vrc),
 
2240
                       E_FAIL);
 
2241
 
 
2242
    /* d is now owned by SVCHelperClientThread(), so release it */
 
2243
    d.release();
 
2244
 
 
2245
    return S_OK;
 
2246
}
 
2247
 
 
2248
/**
 
2249
 *  Worker thread for startSVCHelperClient().
 
2250
 */
 
2251
/* static */
 
2252
DECLCALLBACK(int)
 
2253
VirtualBox::SVCHelperClientThread (RTTHREAD aThread, void *aUser)
 
2254
{
 
2255
    LogFlowFuncEnter();
 
2256
 
 
2257
    std::auto_ptr <StartSVCHelperClientData>
 
2258
        d (static_cast <StartSVCHelperClientData *> (aUser));
 
2259
 
 
2260
    HRESULT rc = S_OK;
 
2261
    bool userFuncCalled = false;
 
2262
 
 
2263
    do
 
2264
    {
 
2265
        AssertBreak (d.get(), rc = E_POINTER);
 
2266
        AssertReturn (!d->progress.isNull(), E_POINTER);
 
2267
 
 
2268
        /* protect VirtualBox from uninitialization */
 
2269
        AutoCaller autoCaller (d->that);
 
2270
        if (!autoCaller.isOk())
 
2271
        {
 
2272
            /* it's too late */
 
2273
            rc = autoCaller.rc();
 
2274
            break;
 
2275
        }
 
2276
 
 
2277
        int vrc = VINF_SUCCESS;
 
2278
 
 
2279
        Guid id;
 
2280
        id.create();
 
2281
        SVCHlpClient client;
 
2282
        vrc = client.create (Utf8StrFmt ("VirtualBox\\SVCHelper\\{%Vuuid}",
 
2283
                                         id.raw()));
 
2284
        if (VBOX_FAILURE (vrc))
 
2285
        {
 
2286
            rc = setError (E_FAIL,
 
2287
                tr ("Could not create the communication channel (%Vrc)"), vrc);
 
2288
            break;
 
2289
        }
 
2290
 
 
2291
        /* get the path to the executable */
 
2292
        char exePathBuf [RTPATH_MAX];
 
2293
        char *exePath = RTProcGetExecutableName (exePathBuf, RTPATH_MAX);
 
2294
        ComAssertBreak (exePath, E_FAIL);
 
2295
 
 
2296
        Utf8Str argsStr = Utf8StrFmt ("/Helper %s", client.name().raw());
 
2297
 
 
2298
        LogFlowFunc (("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
 
2299
 
 
2300
        RTPROCESS pid = NIL_RTPROCESS;
 
2301
 
 
2302
        if (d->privileged)
 
2303
        {
 
2304
            /* Attempt to start a privileged process using the Run As dialog */
 
2305
 
 
2306
            Bstr file = exePath;
 
2307
            Bstr parameters = argsStr;
 
2308
 
 
2309
            SHELLEXECUTEINFO shExecInfo;
 
2310
 
 
2311
            shExecInfo.cbSize = sizeof (SHELLEXECUTEINFO);
 
2312
 
 
2313
            shExecInfo.fMask = NULL;
 
2314
            shExecInfo.hwnd = NULL;
 
2315
            shExecInfo.lpVerb = L"runas";
 
2316
            shExecInfo.lpFile = file;
 
2317
            shExecInfo.lpParameters = parameters;
 
2318
            shExecInfo.lpDirectory = NULL;
 
2319
            shExecInfo.nShow = SW_NORMAL;
 
2320
            shExecInfo.hInstApp = NULL;
 
2321
 
 
2322
            if (!ShellExecuteEx (&shExecInfo))
 
2323
            {
 
2324
                int vrc2 = RTErrConvertFromWin32 (GetLastError());
 
2325
                /* hide excessive details in case of a frequent error
 
2326
                 * (pressing the Cancel button to close the Run As dialog) */
 
2327
                if (vrc2 == VERR_CANCELLED)
 
2328
                    rc = setError (E_FAIL,
 
2329
                        tr ("Operatiion cancelled by the user"));
 
2330
                else
 
2331
                    rc = setError (E_FAIL,
 
2332
                        tr ("Could not launch a privileged process '%s' (%Vrc)"),
 
2333
                        exePath, vrc2);
 
2334
                break;
 
2335
            }
 
2336
        }
 
2337
        else
 
2338
        {
 
2339
            const char *args[] = { exePath, "/Helper", client.name(), 0 };
 
2340
            vrc = RTProcCreate (exePath, args, NULL, 0, &pid);
 
2341
            if (VBOX_FAILURE (vrc))
 
2342
            {
 
2343
                rc = setError (E_FAIL,
 
2344
                    tr ("Could not launch a process '%s' (%Vrc)"), exePath, vrc);
 
2345
                break;
 
2346
            }
 
2347
        }
 
2348
 
 
2349
        /* wait for the client to connect */
 
2350
        vrc = client.connect();
 
2351
        if (VBOX_SUCCESS (vrc))
 
2352
        {
 
2353
            /* start the user supplied function */
 
2354
            rc = d->func (&client, d->progress, d->user, &vrc);
 
2355
            userFuncCalled = true;
 
2356
        }
 
2357
 
 
2358
        /* send the termination signal to the process anyway */
 
2359
        {
 
2360
            int vrc2 = client.write (SVCHlpMsg::Null);
 
2361
            if (VBOX_SUCCESS (vrc))
 
2362
                vrc = vrc2;
 
2363
        }
 
2364
 
 
2365
        if (SUCCEEDED (rc) && VBOX_FAILURE (vrc))
 
2366
        {
 
2367
            rc = setError (E_FAIL,
 
2368
                tr ("Could not operate the communication channel (%Vrc)"), vrc);
 
2369
            break;
 
2370
        }
 
2371
    }
 
2372
    while (0);
 
2373
 
 
2374
    if (FAILED (rc) && !userFuncCalled)
 
2375
    {
 
2376
        /* call the user function in the "cleanup only" mode
 
2377
         * to let it free resources passed to in aUser */
 
2378
        d->func (NULL, NULL, d->user, NULL);
 
2379
    }
 
2380
 
 
2381
    d->progress->notifyComplete (rc);
 
2382
 
 
2383
    LogFlowFuncLeave();
 
2384
    return 0;
 
2385
}
 
2386
 
 
2387
#endif /* RT_OS_WINDOWS */
 
2388
 
 
2389
/**
 
2390
 *  Sends a signal to the client watcher thread to rescan the set of machines
 
2391
 *  that have open sessions.
 
2392
 *
 
2393
 *  @note Doesn't lock anything.
 
2394
 */
 
2395
void VirtualBox::updateClientWatcher()
 
2396
{
 
2397
    AutoCaller autoCaller (this);
 
2398
    AssertComRCReturn (autoCaller.rc(), (void) 0);
 
2399
 
 
2400
    AssertReturn (mWatcherData.mThread != NIL_RTTHREAD, (void) 0);
 
2401
 
 
2402
    /* sent an update request */
 
2403
#if defined(RT_OS_WINDOWS)
 
2404
    ::SetEvent (mWatcherData.mUpdateReq);
 
2405
#elif defined(RT_OS_OS2)
 
2406
    RTSemEventSignal (mWatcherData.mUpdateReq);
 
2407
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
2408
    RTSemEventSignal (mWatcherData.mUpdateReq);
 
2409
#else
 
2410
# error "Port me!"
 
2411
#endif
 
2412
}
 
2413
 
 
2414
/**
 
2415
 *  Adds the given child process ID to the list of processes to be reaped.
 
2416
 *  This call should be followed by #updateClientWatcher() to take the effect.
 
2417
 */
 
2418
void VirtualBox::addProcessToReap (RTPROCESS pid)
 
2419
{
 
2420
    AutoCaller autoCaller (this);
 
2421
    AssertComRCReturn (autoCaller.rc(), (void) 0);
 
2422
 
 
2423
    /// @todo (dmik) Win32?
 
2424
#ifndef RT_OS_WINDOWS
 
2425
    AutoLock alock (this);
 
2426
    mWatcherData.mProcesses.push_back (pid);
 
2427
#endif
 
2428
}
 
2429
 
 
2430
/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
 
2431
struct MachineEvent : public VirtualBox::CallbackEvent
 
2432
{
 
2433
    enum What { DataChanged, StateChanged, Registered };
 
2434
 
 
2435
    MachineEvent (VirtualBox *aVB, const Guid &aId)
 
2436
        : CallbackEvent (aVB), what (DataChanged), id (aId)
 
2437
        {}
 
2438
 
 
2439
    MachineEvent (VirtualBox *aVB, const Guid &aId, MachineState_T aState)
 
2440
        : CallbackEvent (aVB), what (StateChanged), id (aId)
 
2441
        , state (aState)
 
2442
        {}
 
2443
 
 
2444
    MachineEvent (VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
 
2445
        : CallbackEvent (aVB), what (Registered), id (aId)
 
2446
        , registered (aRegistered)
 
2447
        {}
 
2448
 
 
2449
    void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
 
2450
    {
 
2451
        switch (what)
 
2452
        {
 
2453
            case DataChanged:
 
2454
                LogFlow (("OnMachineDataChange: id={%Vuuid}\n", id.ptr()));
 
2455
                aCallback->OnMachineDataChange (id);
 
2456
                break;
 
2457
 
 
2458
            case StateChanged:
 
2459
                LogFlow (("OnMachineStateChange: id={%Vuuid}, state=%d\n",
 
2460
                          id.ptr(), state));
 
2461
                aCallback->OnMachineStateChange (id, state);
 
2462
                break;
 
2463
 
 
2464
            case Registered:
 
2465
                LogFlow (("OnMachineRegistered: id={%Vuuid}, registered=%d\n",
 
2466
                          id.ptr(), registered));
 
2467
                aCallback->OnMachineRegistered (id, registered);
 
2468
                break;
 
2469
        }
 
2470
    }
 
2471
 
 
2472
    const What what;
 
2473
 
 
2474
    Guid id;
 
2475
    MachineState_T state;
 
2476
    BOOL registered;
 
2477
};
 
2478
 
 
2479
/**
 
2480
 *  @note Doesn't lock any object.
 
2481
 */
 
2482
void VirtualBox::onMachineStateChange (const Guid &aId, MachineState_T aState)
 
2483
{
 
2484
    postEvent (new MachineEvent (this, aId, aState));
 
2485
}
 
2486
 
 
2487
/**
 
2488
 *  @note Doesn't lock any object.
 
2489
 */
 
2490
void VirtualBox::onMachineDataChange (const Guid &aId)
 
2491
{
 
2492
    postEvent (new MachineEvent (this, aId));
 
2493
}
 
2494
 
 
2495
/**
 
2496
 *  @note Locks this object for reading.
 
2497
 */
 
2498
BOOL VirtualBox::onExtraDataCanChange (const Guid &aId, INPTR BSTR aKey, INPTR BSTR aValue,
 
2499
                                       Bstr &aError)
 
2500
{
 
2501
    LogFlowThisFunc (("machine={%s} aKey={%ls} aValue={%ls}\n",
 
2502
                      aId.toString().raw(), aKey, aValue));
 
2503
 
 
2504
    AutoCaller autoCaller (this);
 
2505
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
2506
 
 
2507
    CallbackList list;
 
2508
    {
 
2509
        AutoReaderLock alock (this);
 
2510
        list = mData.mCallbacks;
 
2511
    }
 
2512
 
 
2513
    BOOL allowChange = TRUE;
 
2514
    CallbackList::iterator it = list.begin();
 
2515
    while ((it != list.end()) && allowChange)
 
2516
    {
 
2517
        HRESULT rc = (*it++)->OnExtraDataCanChange (aId, aKey, aValue,
 
2518
                                                    aError.asOutParam(), &allowChange);
 
2519
        if (FAILED (rc))
 
2520
        {
 
2521
            /* if a call to this method fails for some reason (for ex., because
 
2522
             * the other side is dead), we ensure allowChange stays true
 
2523
             * (MS COM RPC implementation seems to zero all output vars before
 
2524
             * issuing an IPC call or after a failure, so it's essential
 
2525
             * there) */
 
2526
            allowChange = TRUE;
 
2527
        }
 
2528
    }
 
2529
 
 
2530
    LogFlowThisFunc (("allowChange=%RTbool\n", allowChange));
 
2531
    return allowChange;
 
2532
}
 
2533
 
 
2534
/** Event for onExtraDataChange() */
 
2535
struct ExtraDataEvent : public VirtualBox::CallbackEvent
 
2536
{
 
2537
    ExtraDataEvent (VirtualBox *aVB, const Guid &aMachineId,
 
2538
                    INPTR BSTR aKey, INPTR BSTR aVal)
 
2539
        : CallbackEvent (aVB), machineId (aMachineId)
 
2540
        , key (aKey), val (aVal)
 
2541
        {}
 
2542
 
 
2543
    void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
 
2544
    {
 
2545
        LogFlow (("OnExtraDataChange: machineId={%Vuuid}, key='%ls', val='%ls'\n",
 
2546
                  machineId.ptr(), key.raw(), val.raw()));
 
2547
        aCallback->OnExtraDataChange (machineId, key, val);
 
2548
    }
 
2549
 
 
2550
    Guid machineId;
 
2551
    Bstr key, val;
 
2552
};
 
2553
 
 
2554
/**
 
2555
 *  @note Doesn't lock any object.
 
2556
 */
 
2557
void VirtualBox::onExtraDataChange (const Guid &aId, INPTR BSTR aKey, INPTR BSTR aValue)
 
2558
{
 
2559
    postEvent (new ExtraDataEvent (this, aId, aKey, aValue));
 
2560
}
 
2561
 
 
2562
/**
 
2563
 *  @note Doesn't lock any object.
 
2564
 */
 
2565
void VirtualBox::onMachineRegistered (const Guid &aId, BOOL aRegistered)
 
2566
{
 
2567
    postEvent (new MachineEvent (this, aId, aRegistered));
 
2568
}
 
2569
 
 
2570
/** Event for onSessionStateChange() */
 
2571
struct SessionEvent : public VirtualBox::CallbackEvent
 
2572
{
 
2573
    SessionEvent (VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
 
2574
        : CallbackEvent (aVB), machineId (aMachineId), sessionState (aState)
 
2575
        {}
 
2576
 
 
2577
    void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
 
2578
    {
 
2579
        LogFlow (("OnSessionStateChange: machineId={%Vuuid}, sessionState=%d\n",
 
2580
                  machineId.ptr(), sessionState));
 
2581
        aCallback->OnSessionStateChange (machineId, sessionState);
 
2582
    }
 
2583
 
 
2584
    Guid machineId;
 
2585
    SessionState_T sessionState;
 
2586
};
 
2587
 
 
2588
/**
 
2589
 *  @note Doesn't lock any object.
 
2590
 */
 
2591
void VirtualBox::onSessionStateChange (const Guid &aId, SessionState_T aState)
 
2592
{
 
2593
    postEvent (new SessionEvent (this, aId, aState));
 
2594
}
 
2595
 
 
2596
/** Event for onSnapshotTaken(), onSnapshotRemoved() and onSnapshotChange() */
 
2597
struct SnapshotEvent : public VirtualBox::CallbackEvent
 
2598
{
 
2599
    enum What { Taken, Discarded, Changed };
 
2600
 
 
2601
    SnapshotEvent (VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
 
2602
                   What aWhat)
 
2603
        : CallbackEvent (aVB)
 
2604
        , what (aWhat)
 
2605
        , machineId (aMachineId), snapshotId (aSnapshotId)
 
2606
        {}
 
2607
 
 
2608
    void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
 
2609
    {
 
2610
        switch (what)
 
2611
        {
 
2612
            case Taken:
 
2613
                LogFlow (("OnSnapshotTaken: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
 
2614
                          machineId.ptr(), snapshotId.ptr()));
 
2615
                aCallback->OnSnapshotTaken (machineId, snapshotId);
 
2616
                break;
 
2617
 
 
2618
            case Discarded:
 
2619
                LogFlow (("OnSnapshotDiscarded: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
 
2620
                          machineId.ptr(), snapshotId.ptr()));
 
2621
                aCallback->OnSnapshotDiscarded (machineId, snapshotId);
 
2622
                break;
 
2623
 
 
2624
            case Changed:
 
2625
                LogFlow (("OnSnapshotChange: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
 
2626
                          machineId.ptr(), snapshotId.ptr()));
 
2627
                aCallback->OnSnapshotChange (machineId, snapshotId);
 
2628
                break;
 
2629
        }
 
2630
    }
 
2631
 
 
2632
    const What what;
 
2633
 
 
2634
    Guid machineId;
 
2635
    Guid snapshotId;
 
2636
};
 
2637
 
 
2638
/**
 
2639
 *  @note Doesn't lock any object.
 
2640
 */
 
2641
void VirtualBox::onSnapshotTaken (const Guid &aMachineId, const Guid &aSnapshotId)
 
2642
{
 
2643
    postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Taken));
 
2644
}
 
2645
 
 
2646
/**
 
2647
 *  @note Doesn't lock any object.
 
2648
 */
 
2649
void VirtualBox::onSnapshotDiscarded (const Guid &aMachineId, const Guid &aSnapshotId)
 
2650
{
 
2651
    postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Discarded));
 
2652
}
 
2653
 
 
2654
/**
 
2655
 *  @note Doesn't lock any object.
 
2656
 */
 
2657
void VirtualBox::onSnapshotChange (const Guid &aMachineId, const Guid &aSnapshotId)
 
2658
{
 
2659
    postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Changed));
 
2660
}
 
2661
 
 
2662
/**
 
2663
 *  @note Locks this object for reading.
 
2664
 */
 
2665
ComObjPtr <GuestOSType> VirtualBox::getUnknownOSType()
 
2666
{
 
2667
    ComObjPtr <GuestOSType> type;
 
2668
 
 
2669
    AutoCaller autoCaller (this);
 
2670
    AssertComRCReturn (autoCaller.rc(), type);
 
2671
 
 
2672
    AutoReaderLock alock (this);
 
2673
 
 
2674
    /* unknown type must always be the first */
 
2675
    ComAssertRet (mData.mGuestOSTypes.size() > 0, type);
 
2676
 
 
2677
    type = mData.mGuestOSTypes.front();
 
2678
    return type;
 
2679
}
 
2680
 
 
2681
/**
 
2682
 *  Returns the list of opened machines (i.e. machines having direct sessions
 
2683
 *  opened by client processes).
 
2684
 *
 
2685
 *  @note the returned list contains smart pointers. So, clear it as soon as
 
2686
 *  it becomes no more necessary to release instances.
 
2687
 *  @note it can be possible that a session machine from the list has been
 
2688
 *  already uninitialized, so a) lock the instance and b) chheck for
 
2689
 *  instance->isReady() return value before manipulating the object directly
 
2690
 *  (i.e. not through COM methods).
 
2691
 *
 
2692
 *  @note Locks objects for reading.
 
2693
 */
 
2694
void VirtualBox::getOpenedMachines (SessionMachineVector &aVector)
 
2695
{
 
2696
    AutoCaller autoCaller (this);
 
2697
    AssertComRCReturn (autoCaller.rc(), (void) 0);
 
2698
 
 
2699
    std::list <ComObjPtr <SessionMachine> > list;
 
2700
 
 
2701
    {
 
2702
        AutoReaderLock alock (this);
 
2703
 
 
2704
        for (MachineList::iterator it = mData.mMachines.begin();
 
2705
             it != mData.mMachines.end();
 
2706
             ++ it)
 
2707
        {
 
2708
            ComObjPtr <SessionMachine> sm = (*it)->sessionMachine();
 
2709
            /* SessionMachine is null when there are no open sessions */
 
2710
            if (!sm.isNull())
 
2711
                list.push_back (sm);
 
2712
        }
 
2713
    }
 
2714
 
 
2715
    aVector = SessionMachineVector (list.begin(), list.end());
 
2716
    return;
 
2717
}
 
2718
 
 
2719
/**
 
2720
 *  Helper to find machines that use the given DVD image.
 
2721
 *
 
2722
 *  @param machineIDs   string where to store the list (can be NULL)
 
2723
 *  @return TRUE if at least one machine found and false otherwise
 
2724
 *
 
2725
 *  @note For now, we just scan all the machines. We can optimize this later
 
2726
 *  if required by adding the corresponding field to DVDImage and requiring all
 
2727
 *  IDVDImage instances to be DVDImage objects.
 
2728
 *
 
2729
 *  @note Locks objects for reading.
 
2730
 */
 
2731
BOOL VirtualBox::getDVDImageUsage (const Guid &id,
 
2732
                                   ResourceUsage_T usage,
 
2733
                                   Bstr *machineIDs)
 
2734
{
 
2735
    AutoCaller autoCaller (this);
 
2736
    AssertComRCReturn (autoCaller.rc(), FALSE);
 
2737
 
 
2738
    typedef std::set <Guid> Set;
 
2739
    Set idSet;
 
2740
 
 
2741
    {
 
2742
        AutoReaderLock alock (this);
 
2743
 
 
2744
        for (MachineList::const_iterator mit = mData.mMachines.begin();
 
2745
             mit != mData.mMachines.end();
 
2746
             ++ mit)
 
2747
        {
 
2748
            /// @todo (dmik) move this part to Machine for better incapsulation
 
2749
 
 
2750
            ComObjPtr <Machine> m = *mit;
 
2751
            AutoReaderLock malock (m);
 
2752
 
 
2753
            /* take the session machine when appropriate */
 
2754
            if (!m->data()->mSession.mMachine.isNull())
 
2755
                m = m->data()->mSession.mMachine;
 
2756
 
 
2757
            const ComObjPtr <DVDDrive> &dvd = m->dvdDrive();
 
2758
            AutoReaderLock dalock (dvd);
 
2759
 
 
2760
            /* loop over the backed up (permanent) and current (temporary) dvd data */
 
2761
            DVDDrive::Data *dvdData [2];
 
2762
            if (dvd->data().isBackedUp())
 
2763
            {
 
2764
                dvdData [0] = dvd->data().backedUpData();
 
2765
                dvdData [1] = dvd->data().data();
 
2766
            }
 
2767
            else
 
2768
            {
 
2769
                dvdData [0] = dvd->data().data();
 
2770
                dvdData [1] = NULL;
 
2771
            }
 
2772
 
 
2773
            if (!(usage & ResourceUsage_PermanentUsage))
 
2774
                dvdData [0] = NULL;
 
2775
            if (!(usage & ResourceUsage_TemporaryUsage))
 
2776
                dvdData [1] = NULL;
 
2777
 
 
2778
            for (unsigned i = 0; i < ELEMENTS (dvdData); i++)
 
2779
            {
 
2780
                if (dvdData [i])
 
2781
                {
 
2782
                    if (dvdData [i]->mDriveState == DriveState_ImageMounted)
 
2783
                    {
 
2784
                        Guid iid;
 
2785
                        dvdData [i]->mDVDImage->COMGETTER(Id) (iid.asOutParam());
 
2786
                        if (iid == id)
 
2787
                            idSet.insert (m->data()->mUuid);
 
2788
                    }
 
2789
                }
 
2790
            }
 
2791
        }
 
2792
    }
 
2793
 
 
2794
    if (machineIDs)
 
2795
    {
 
2796
        if (!idSet.empty())
 
2797
        {
 
2798
            /* convert to a string of UUIDs */
 
2799
            char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
 
2800
            char *idListPtr = idList;
 
2801
            for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
 
2802
            {
 
2803
                RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
 
2804
                idListPtr += RTUUID_STR_LENGTH - 1;
 
2805
                /* replace EOS with a space char */
 
2806
                *(idListPtr ++) = ' ';
 
2807
            }
 
2808
            Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
 
2809
            /* remove the trailing space */
 
2810
            *(-- idListPtr) = 0;
 
2811
            /* copy the string */
 
2812
            *machineIDs = idList;
 
2813
            RTMemTmpFree (idList);
 
2814
        }
 
2815
        else
 
2816
        {
 
2817
            (*machineIDs).setNull();
 
2818
        }
 
2819
    }
 
2820
 
 
2821
    return !idSet.empty();
 
2822
}
 
2823
 
 
2824
/**
 
2825
 *  Helper to find machines that use the given floppy image.
 
2826
 *
 
2827
 *  @param machineIDs   string where to store the list (can be NULL)
 
2828
 *  @return TRUE if at least one machine found and false otherwise
 
2829
 *
 
2830
 *  @note For now, we just scan all the machines. We can optimize this later
 
2831
 *  if required by adding the corresponding field to FloppyImage and requiring all
 
2832
 *  IFloppyImage instances to be FloppyImage objects.
 
2833
 *
 
2834
 *  @note Locks objects for reading.
 
2835
 */
 
2836
BOOL VirtualBox::getFloppyImageUsage (const Guid &id,
 
2837
                                      ResourceUsage_T usage,
 
2838
                                      Bstr *machineIDs)
 
2839
{
 
2840
    AutoCaller autoCaller (this);
 
2841
    AssertComRCReturn (autoCaller.rc(), FALSE);
 
2842
 
 
2843
    typedef std::set <Guid> Set;
 
2844
    Set idSet;
 
2845
 
 
2846
    {
 
2847
        AutoReaderLock alock (this);
 
2848
 
 
2849
        for (MachineList::const_iterator mit = mData.mMachines.begin();
 
2850
             mit != mData.mMachines.end();
 
2851
             ++ mit)
 
2852
        {
 
2853
            /// @todo (dmik) move this part to Machine for better incapsulation
 
2854
 
 
2855
            ComObjPtr <Machine> m = *mit;
 
2856
            AutoReaderLock malock (m);
 
2857
 
 
2858
            /* take the session machine when appropriate */
 
2859
            if (!m->data()->mSession.mMachine.isNull())
 
2860
                m = m->data()->mSession.mMachine;
 
2861
 
 
2862
            const ComObjPtr <FloppyDrive> &drv = m->floppyDrive();
 
2863
            AutoReaderLock dalock (drv);
 
2864
 
 
2865
            /* loop over the backed up (permanent) and current (temporary) floppy data */
 
2866
            FloppyDrive::Data *data [2];
 
2867
            if (drv->data().isBackedUp())
 
2868
            {
 
2869
                data [0] = drv->data().backedUpData();
 
2870
                data [1] = drv->data().data();
 
2871
            }
 
2872
            else
 
2873
            {
 
2874
                data [0] = drv->data().data();
 
2875
                data [1] = NULL;
 
2876
            }
 
2877
 
 
2878
            if (!(usage & ResourceUsage_PermanentUsage))
 
2879
                data [0] = NULL;
 
2880
            if (!(usage & ResourceUsage_TemporaryUsage))
 
2881
                data [1] = NULL;
 
2882
 
 
2883
            for (unsigned i = 0; i < ELEMENTS (data); i++)
 
2884
            {
 
2885
                if (data [i])
 
2886
                {
 
2887
                    if (data [i]->mDriveState == DriveState_ImageMounted)
 
2888
                    {
 
2889
                        Guid iid;
 
2890
                        data [i]->mFloppyImage->COMGETTER(Id) (iid.asOutParam());
 
2891
                        if (iid == id)
 
2892
                            idSet.insert (m->data()->mUuid);
 
2893
                    }
 
2894
                }
 
2895
            }
 
2896
        }
 
2897
    }
 
2898
 
 
2899
    if (machineIDs)
 
2900
    {
 
2901
        if (!idSet.empty())
 
2902
        {
 
2903
            /* convert to a string of UUIDs */
 
2904
            char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
 
2905
            char *idListPtr = idList;
 
2906
            for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
 
2907
            {
 
2908
                RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
 
2909
                idListPtr += RTUUID_STR_LENGTH - 1;
 
2910
                /* replace EOS with a space char */
 
2911
                *(idListPtr ++) = ' ';
 
2912
            }
 
2913
            Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
 
2914
            /* remove the trailing space */
 
2915
            *(-- idListPtr) = 0;
 
2916
            /* copy the string */
 
2917
            *machineIDs = idList;
 
2918
            RTMemTmpFree (idList);
 
2919
        }
 
2920
        else
 
2921
        {
 
2922
            (*machineIDs).setNull();
 
2923
        }
 
2924
    }
 
2925
 
 
2926
    return !idSet.empty();
 
2927
}
 
2928
 
 
2929
/**
 
2930
 *  Tries to calculate the relative path of the given absolute path using the
 
2931
 *  directory of the VirtualBox settings file as the base directory.
 
2932
 *
 
2933
 *  @param  aPath   absolute path to calculate the relative path for
 
2934
 *  @param  aResult where to put the result (used only when it's possible to
 
2935
 *                  make a relative path from the given absolute path;
 
2936
 *                  otherwise left untouched)
 
2937
 *
 
2938
 *  @note Doesn't lock any object.
 
2939
 */
 
2940
void VirtualBox::calculateRelativePath (const char *aPath, Utf8Str &aResult)
 
2941
{
 
2942
    AutoCaller autoCaller (this);
 
2943
    AssertComRCReturn (autoCaller.rc(), (void) 0);
 
2944
 
 
2945
    /* no need to lock since mHomeDir is const */
 
2946
 
 
2947
    Utf8Str settingsDir = mData.mHomeDir;
 
2948
 
 
2949
    if (RTPathStartsWith (aPath, settingsDir))
 
2950
    {
 
2951
        /* when assigning, we create a separate Utf8Str instance because both
 
2952
         * aPath and aResult can point to the same memory location when this
 
2953
         * func is called (if we just do aResult = aPath, aResult will be freed
 
2954
         * first, and since its the same as aPath, an attempt to copy garbage
 
2955
         * will be made. */
 
2956
        aResult = Utf8Str (aPath + settingsDir.length() + 1);
 
2957
    }
 
2958
}
 
2959
 
 
2960
// private methods
 
2961
/////////////////////////////////////////////////////////////////////////////
 
2962
 
 
2963
/**
 
2964
 *  Searches for a Machine object with the given ID in the collection
 
2965
 *  of registered machines.
 
2966
 *
 
2967
 *  @param id
 
2968
 *      ID of the machine
 
2969
 *  @param doSetError
 
2970
 *      if TRUE, the appropriate error info is set in case when the machine
 
2971
 *      is not found
 
2972
 *  @param machine
 
2973
 *      where to store the found machine object (can be NULL)
 
2974
 *
 
2975
 *  @return
 
2976
 *      S_OK when found or E_INVALIDARG when not found
 
2977
 *
 
2978
 *  @note Locks this object for reading.
 
2979
 */
 
2980
HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
 
2981
                                 ComObjPtr <Machine> *aMachine /* = NULL */)
 
2982
{
 
2983
    AutoCaller autoCaller (this);
 
2984
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
2985
 
 
2986
    bool found = false;
 
2987
 
 
2988
    {
 
2989
        AutoReaderLock alock (this);
 
2990
 
 
2991
        for (MachineList::iterator it = mData.mMachines.begin();
 
2992
             !found && it != mData.mMachines.end();
 
2993
             ++ it)
 
2994
        {
 
2995
            /* mUuid is constant, no need to lock */
 
2996
            found = (*it)->data()->mUuid == aId;
 
2997
            if (found && aMachine)
 
2998
                *aMachine = *it;
 
2999
        }
 
3000
    }
 
3001
 
 
3002
    HRESULT rc = found ? S_OK : E_INVALIDARG;
 
3003
 
 
3004
    if (aSetError && !found)
 
3005
    {
 
3006
        setError (E_INVALIDARG,
 
3007
            tr ("Could not find a registered machine with UUID {%Vuuid}"),
 
3008
            aId.raw());
 
3009
    }
 
3010
 
 
3011
    return rc;
 
3012
}
 
3013
 
 
3014
/**
 
3015
 *  Searches for a HardDisk object with the given ID or location specification
 
3016
 *  in the collection of registered hard disks. If both ID and location are
 
3017
 *  specified, the first object that matches either of them (not necessarily
 
3018
 *  both) is returned.
 
3019
 *
 
3020
 *  @param aId          ID of the hard disk (NULL when unused)
 
3021
 *  @param aLocation    full location specification (NULL when unused)
 
3022
 *  @param aSetError    if TRUE, the appropriate error info is set in case when
 
3023
 *                      the disk is not found and only one search criteria (ID
 
3024
 *                      or file name) is specified.
 
3025
 *  @param aHardDisk    where to store the found hard disk object (can be NULL)
 
3026
 *
 
3027
 *  @return
 
3028
 *      S_OK when found or E_INVALIDARG when not found
 
3029
 *
 
3030
 *  @note Locks objects for reading!
 
3031
 */
 
3032
HRESULT VirtualBox::
 
3033
findHardDisk (const Guid *aId, const BSTR aLocation,
 
3034
              bool aSetError, ComObjPtr <HardDisk> *aHardDisk /* = NULL */)
 
3035
{
 
3036
    ComAssertRet (aId || aLocation, E_INVALIDARG);
 
3037
 
 
3038
    AutoReaderLock alock (this);
 
3039
 
 
3040
    /* first lookup the map by UUID if UUID is provided */
 
3041
    if (aId)
 
3042
    {
 
3043
        HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
 
3044
        if (it != mData.mHardDiskMap.end())
 
3045
        {
 
3046
            if (aHardDisk)
 
3047
                *aHardDisk = (*it).second;
 
3048
            return S_OK;
 
3049
        }
 
3050
    }
 
3051
 
 
3052
    /* then iterate and find by location */
 
3053
    bool found = false;
 
3054
    if (aLocation)
 
3055
    {
 
3056
        Utf8Str location = aLocation;
 
3057
 
 
3058
        for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
 
3059
             !found && it != mData.mHardDiskMap.end();
 
3060
             ++ it)
 
3061
        {
 
3062
            const ComObjPtr <HardDisk> &hd = (*it).second;
 
3063
            AutoReaderLock hdLock (hd);
 
3064
 
 
3065
            if (hd->storageType() == HardDiskStorageType_VirtualDiskImage ||
 
3066
                hd->storageType() == HardDiskStorageType_VMDKImage)
 
3067
            {
 
3068
                /* locations of VDI and VMDK hard disks for now are just
 
3069
                 * file paths */
 
3070
                found = RTPathCompare (location,
 
3071
                                       Utf8Str (hd->toString
 
3072
                                                (false /* aShort */))) == 0;
 
3073
            }
 
3074
            else
 
3075
            {
 
3076
                found = aLocation == hd->toString (false /* aShort */);
 
3077
            }
 
3078
 
 
3079
            if (found && aHardDisk)
 
3080
                *aHardDisk = hd;
 
3081
        }
 
3082
    }
 
3083
 
 
3084
    HRESULT rc = found ? S_OK : E_INVALIDARG;
 
3085
 
 
3086
    if (aSetError && !found)
 
3087
    {
 
3088
        if (aId && !aLocation)
 
3089
            setError (rc, tr ("Could not find a registered hard disk "
 
3090
                              "with UUID {%Vuuid}"), aId->raw());
 
3091
        else if (aLocation && !aId)
 
3092
            setError (rc, tr ("Could not find a registered hard disk "
 
3093
                              "with location '%ls'"), aLocation);
 
3094
    }
 
3095
 
 
3096
    return rc;
 
3097
}
 
3098
 
 
3099
/**
 
3100
 *  @deprecated Use #findHardDisk() instead.
 
3101
 *
 
3102
 *  Searches for a HVirtualDiskImage object with the given ID or file path in the
 
3103
 *  collection of registered hard disks. If both ID and file path are specified,
 
3104
 *  the first object that matches either of them (not necessarily both)
 
3105
 *  is returned.
 
3106
 *
 
3107
 *  @param aId          ID of the hard disk (NULL when unused)
 
3108
 *  @param filePathFull full path to the image file (NULL when unused)
 
3109
 *  @param aSetError    if TRUE, the appropriate error info is set in case when
 
3110
 *                      the disk is not found and only one search criteria (ID
 
3111
 *                      or file name) is specified.
 
3112
 *  @param aHardDisk    where to store the found hard disk object (can be NULL)
 
3113
 *
 
3114
 *  @return
 
3115
 *      S_OK when found or E_INVALIDARG when not found
 
3116
 *
 
3117
 *  @note Locks objects for reading!
 
3118
 */
 
3119
HRESULT VirtualBox::
 
3120
findVirtualDiskImage (const Guid *aId, const BSTR aFilePathFull,
 
3121
                      bool aSetError, ComObjPtr <HVirtualDiskImage> *aImage /* = NULL */)
 
3122
{
 
3123
    ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
 
3124
 
 
3125
    AutoReaderLock alock (this);
 
3126
 
 
3127
    /* first lookup the map by UUID if UUID is provided */
 
3128
    if (aId)
 
3129
    {
 
3130
        HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
 
3131
        if (it != mData.mHardDiskMap.end())
 
3132
        {
 
3133
            AutoReaderLock hdLock ((*it).second);
 
3134
            if ((*it).second->storageType() == HardDiskStorageType_VirtualDiskImage)
 
3135
            {
 
3136
                if (aImage)
 
3137
                    *aImage = (*it).second->asVDI();
 
3138
                return S_OK;
 
3139
            }
 
3140
        }
 
3141
    }
 
3142
 
 
3143
    /* then iterate and find by name */
 
3144
    bool found = false;
 
3145
    if (aFilePathFull)
 
3146
    {
 
3147
        for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
 
3148
             !found && it != mData.mHardDiskMap.end();
 
3149
             ++ it)
 
3150
        {
 
3151
            const ComObjPtr <HardDisk> &hd = (*it).second;
 
3152
            AutoReaderLock hdLock (hd);
 
3153
            if (hd->storageType() != HardDiskStorageType_VirtualDiskImage)
 
3154
                continue;
 
3155
 
 
3156
            found = RTPathCompare (Utf8Str (aFilePathFull),
 
3157
                                   Utf8Str (hd->asVDI()->filePathFull())) == 0;
 
3158
            if (found && aImage)
 
3159
                *aImage = hd->asVDI();
 
3160
        }
 
3161
    }
 
3162
 
 
3163
    HRESULT rc = found ? S_OK : E_INVALIDARG;
 
3164
 
 
3165
    if (aSetError && !found)
 
3166
    {
 
3167
        if (aId && !aFilePathFull)
 
3168
            setError (rc, tr ("Could not find a registered VDI hard disk "
 
3169
                              "with UUID {%Vuuid}"), aId->raw());
 
3170
        else if (aFilePathFull && !aId)
 
3171
            setError (rc, tr ("Could not find a registered VDI hard disk "
 
3172
                              "with the file path '%ls'"), aFilePathFull);
 
3173
    }
 
3174
 
 
3175
    return rc;
 
3176
}
 
3177
 
 
3178
/**
 
3179
 *  Searches for a DVDImage object with the given ID or file path in the
 
3180
 *  collection of registered DVD images. If both ID and file path are specified,
 
3181
 *  the first object that matches either of them (not necessarily both)
 
3182
 *  is returned.
 
3183
 *
 
3184
 *  @param aId
 
3185
 *      ID of the DVD image (unused when NULL)
 
3186
 *  @param aFilePathFull
 
3187
 *      full path to the image file (unused when NULL)
 
3188
 *  @param aSetError
 
3189
 *      if TRUE, the appropriate error info is set in case when the image is not
 
3190
 *      found and only one search criteria (ID or file name) is specified.
 
3191
 *  @param aImage
 
3192
 *      where to store the found DVD image object (can be NULL)
 
3193
 *
 
3194
 *  @return
 
3195
 *      S_OK when found or E_INVALIDARG when not found
 
3196
 *
 
3197
 *  @note Locks this object for reading.
 
3198
 */
 
3199
HRESULT VirtualBox::findDVDImage (const Guid *aId, const BSTR aFilePathFull,
 
3200
                                  bool aSetError,
 
3201
                                  ComObjPtr <DVDImage> *aImage /* = NULL */)
 
3202
{
 
3203
    ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
 
3204
 
 
3205
    bool found = false;
 
3206
 
 
3207
    {
 
3208
        AutoReaderLock alock (this);
 
3209
 
 
3210
        for (DVDImageList::const_iterator it = mData.mDVDImages.begin();
 
3211
             !found && it != mData.mDVDImages.end();
 
3212
             ++ it)
 
3213
        {
 
3214
            /* DVDImage fields are constant, so no need to lock  */
 
3215
            found = (aId && (*it)->id() == *aId) ||
 
3216
                    (aFilePathFull &&
 
3217
                     RTPathCompare (Utf8Str (aFilePathFull),
 
3218
                                    Utf8Str ((*it)->filePathFull())) == 0);
 
3219
            if (found && aImage)
 
3220
                *aImage = *it;
 
3221
        }
 
3222
    }
 
3223
 
 
3224
    HRESULT rc = found ? S_OK : E_INVALIDARG;
 
3225
 
 
3226
    if (aSetError && !found)
 
3227
    {
 
3228
        if (aId && !aFilePathFull)
 
3229
            setError (rc, tr ("Could not find a registered CD/DVD image "
 
3230
                              "with UUID {%s}"), aId->toString().raw());
 
3231
        else if (aFilePathFull && !aId)
 
3232
            setError (rc, tr ("Could not find a registered CD/DVD image "
 
3233
                              "with the file path '%ls'"), aFilePathFull);
 
3234
    }
 
3235
 
 
3236
    return rc;
 
3237
}
 
3238
 
 
3239
/**
 
3240
 *  Searches for a FloppyImage object with the given ID or file path in the
 
3241
 *  collection of registered floppy images. If both ID and file path are specified,
 
3242
 *  the first object that matches either of them (not necessarily both)
 
3243
 *  is returned.
 
3244
 *
 
3245
 *  @param aId
 
3246
 *      ID of the floppy image (unused when NULL)
 
3247
 *  @param aFilePathFull
 
3248
 *      full path to the image file (unused when NULL)
 
3249
 *  @param aSetError
 
3250
 *      if TRUE, the appropriate error info is set in case when the image is not
 
3251
 *      found and only one search criteria (ID or file name) is specified.
 
3252
 *  @param aImage
 
3253
 *      where to store the found floppy image object (can be NULL)
 
3254
 *
 
3255
 *  @return
 
3256
 *      S_OK when found or E_INVALIDARG when not found
 
3257
 *
 
3258
 *  @note Locks this object for reading.
 
3259
 */
 
3260
HRESULT VirtualBox::findFloppyImage (const Guid *aId, const BSTR aFilePathFull,
 
3261
                                     bool aSetError,
 
3262
                                     ComObjPtr <FloppyImage> *aImage /* = NULL */)
 
3263
{
 
3264
    ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
 
3265
 
 
3266
    bool found = false;
 
3267
 
 
3268
    {
 
3269
        AutoReaderLock alock (this);
 
3270
 
 
3271
        for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
 
3272
             !found && it != mData.mFloppyImages.end();
 
3273
             ++ it)
 
3274
        {
 
3275
            /* FloppyImage fields are constant, so no need to lock  */
 
3276
            found = (aId && (*it)->id() == *aId) ||
 
3277
                    (aFilePathFull &&
 
3278
                     RTPathCompare (Utf8Str (aFilePathFull),
 
3279
                                    Utf8Str ((*it)->filePathFull())) == 0);
 
3280
            if (found && aImage)
 
3281
                *aImage = *it;
 
3282
        }
 
3283
    }
 
3284
 
 
3285
    HRESULT rc = found ? S_OK : E_INVALIDARG;
 
3286
 
 
3287
    if (aSetError && !found)
 
3288
    {
 
3289
        if (aId && !aFilePathFull)
 
3290
            setError (rc, tr ("Could not find a registered floppy image "
 
3291
                              "with UUID {%s}"), aId->toString().raw());
 
3292
        else if (aFilePathFull && !aId)
 
3293
            setError (rc, tr ("Could not find a registered floppy image "
 
3294
                              "with the file path '%ls'"), aFilePathFull);
 
3295
    }
 
3296
 
 
3297
    return rc;
 
3298
}
 
3299
 
 
3300
/**
 
3301
 *  When \a aHardDisk is not NULL, searches for an object equal to the given
 
3302
 *  hard disk in the collection of registered hard disks, or, if the given hard
 
3303
 *  disk is HVirtualDiskImage, for an object with the given file path in the
 
3304
 *  collection of all registered non-hard disk images (DVDs and floppies).
 
3305
 *  Other parameters are unused.
 
3306
 *
 
3307
 *  When \a aHardDisk is NULL, searches for an object with the given ID or file
 
3308
 *  path in the collection of all registered images (VDIs, DVDs and floppies).
 
3309
 *  If both ID and file path are specified, matching either of them will satisfy
 
3310
 *  the search.
 
3311
 *
 
3312
 *  If a matching object is found, this method returns E_INVALIDARG and sets the
 
3313
 *  appropriate error info. Otherwise, S_OK is returned.
 
3314
 *
 
3315
 *  @param aHardDisk        hard disk object to check against registered media
 
3316
 *                          (NULL when unused)
 
3317
 *  @param aId              UUID of the media to check (NULL when unused)
 
3318
 *  @param aFilePathFull    full path to the image file (NULL when unused)
 
3319
 *
 
3320
 *  @note Locks objects!
 
3321
 */
 
3322
HRESULT VirtualBox::checkMediaForConflicts (HardDisk *aHardDisk,
 
3323
                                            const Guid *aId,
 
3324
                                            const BSTR aFilePathFull)
 
3325
{
 
3326
    AssertReturn (aHardDisk || aId || aFilePathFull, E_FAIL);
 
3327
 
 
3328
    HRESULT rc = S_OK;
 
3329
 
 
3330
    AutoReaderLock alock (this);
 
3331
 
 
3332
    if (aHardDisk)
 
3333
    {
 
3334
        for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
 
3335
             it != mData.mHardDiskMap.end();
 
3336
             ++ it)
 
3337
        {
 
3338
            const ComObjPtr <HardDisk> &hd = (*it).second;
 
3339
            if (hd->sameAs (aHardDisk))
 
3340
                return setError (E_INVALIDARG,
 
3341
                    tr ("A hard disk with UUID {%Vuuid} or with the same properties "
 
3342
                        "('%ls') is already registered"),
 
3343
                        aHardDisk->id().raw(), aHardDisk->toString().raw());
 
3344
        }
 
3345
 
 
3346
        aId = &aHardDisk->id();
 
3347
        if (aHardDisk->storageType() == HardDiskStorageType_VirtualDiskImage)
 
3348
#if !defined (VBOX_WITH_XPCOM)
 
3349
#if defined(RT_OS_WINDOWS)
 
3350
            /// @todo (dmik) stupid BSTR declaration lacks the BCSTR counterpart
 
3351
            const_cast <BSTR> (aFilePathFull) = aHardDisk->asVDI()->filePathFull();
 
3352
#endif
 
3353
#else
 
3354
            aFilePathFull = aHardDisk->asVDI()->filePathFull();
 
3355
#endif
 
3356
    }
 
3357
 
 
3358
    bool found = false;
 
3359
 
 
3360
    if (aId || aFilePathFull) do
 
3361
    {
 
3362
        if (!aHardDisk)
 
3363
        {
 
3364
            rc = findHardDisk (aId, aFilePathFull, false /* aSetError */);
 
3365
            found = SUCCEEDED (rc);
 
3366
            if (found)
 
3367
                break;
 
3368
        }
 
3369
 
 
3370
        rc = findDVDImage (aId, aFilePathFull, false /* aSetError */);
 
3371
        found = SUCCEEDED (rc);
 
3372
        if (found)
 
3373
            break;
 
3374
 
 
3375
        rc = findFloppyImage (aId, aFilePathFull, false /* aSetError */);
 
3376
        found = SUCCEEDED (rc);
 
3377
        if (found)
 
3378
            break;
 
3379
    }
 
3380
    while (0);
 
3381
 
 
3382
    if (found)
 
3383
    {
 
3384
        if (aId && !aFilePathFull)
 
3385
            rc = setError (E_INVALIDARG,
 
3386
                tr ("A disk image with UUID {%Vuuid} is already registered"),
 
3387
                aId->raw());
 
3388
        else if (aFilePathFull && !aId)
 
3389
            rc = setError (E_INVALIDARG,
 
3390
                tr ("A disk image with file path '%ls' is already registered"),
 
3391
                aFilePathFull);
 
3392
        else
 
3393
            rc = setError (E_INVALIDARG,
 
3394
                tr ("A disk image with UUID {%Vuuid} or file path '%ls' "
 
3395
                    "is already registered"), aId->raw(), aFilePathFull);
 
3396
    }
 
3397
    else
 
3398
        rc = S_OK;
 
3399
 
 
3400
    return rc;
 
3401
}
 
3402
 
 
3403
/**
 
3404
 *  Reads in the machine definitions from the configuration loader
 
3405
 *  and creates the relevant objects.
 
3406
 *
 
3407
 *  @note Can be called only from #init().
 
3408
 *  @note Doesn't lock anything.
 
3409
 */
 
3410
HRESULT VirtualBox::loadMachines (CFGNODE aGlobal)
 
3411
{
 
3412
    AutoCaller autoCaller (this);
 
3413
    AssertReturn (autoCaller.state() == InInit, E_FAIL);
 
3414
 
 
3415
    HRESULT rc = S_OK;
 
3416
    CFGNODE machineRegistry = 0;
 
3417
    unsigned count = 0;
 
3418
 
 
3419
    CFGLDRGetChildNode (aGlobal, "MachineRegistry", 0, &machineRegistry);
 
3420
    Assert (machineRegistry);
 
3421
 
 
3422
    CFGLDRCountChildren(machineRegistry, "MachineEntry", &count);
 
3423
    for (unsigned i = 0; i < count && SUCCEEDED (rc); i++)
 
3424
    {
 
3425
        CFGNODE vm = 0;
 
3426
        CFGLDRGetChildNode(machineRegistry, "MachineEntry", i, &vm);
 
3427
        /* get the UUID */
 
3428
        Guid uuid;
 
3429
        CFGLDRQueryUUID(vm, "uuid", uuid.ptr());
 
3430
        /* get the machine configuration file name */
 
3431
        Bstr src;
 
3432
        CFGLDRQueryBSTR(vm, "src", src.asOutParam());
 
3433
 
 
3434
        /* create a new object */
 
3435
        ComObjPtr <Machine> machine;
 
3436
        rc = machine.createObject();
 
3437
        if (SUCCEEDED (rc))
 
3438
        {
 
3439
            /* initialize the machine object and register it */
 
3440
            rc = machine->init (this, src, Machine::Init_Registered,
 
3441
                                NULL, FALSE, &uuid);
 
3442
            if (SUCCEEDED (rc))
 
3443
                rc = registerMachine (machine);
 
3444
        }
 
3445
 
 
3446
        CFGLDRReleaseNode(vm);
 
3447
    }
 
3448
 
 
3449
    CFGLDRReleaseNode(machineRegistry);
 
3450
 
 
3451
    return rc;
 
3452
}
 
3453
 
 
3454
/**
 
3455
 *  Reads in the disk registration entries from the global settings file
 
3456
 *  and creates the relevant objects
 
3457
 *
 
3458
 *  @param aGlobal  <Global> node
 
3459
 *
 
3460
 *  @note Can be called only from #init().
 
3461
 *  @note Doesn't lock anything.
 
3462
 */
 
3463
HRESULT VirtualBox::loadDisks (CFGNODE aGlobal)
 
3464
{
 
3465
    AutoCaller autoCaller (this);
 
3466
    AssertReturn (autoCaller.state() == InInit, E_FAIL);
 
3467
 
 
3468
    HRESULT rc = S_OK;
 
3469
    CFGNODE registryNode = 0;
 
3470
 
 
3471
    CFGLDRGetChildNode (aGlobal, "DiskRegistry", 0, &registryNode);
 
3472
    ComAssertRet (registryNode, E_FAIL);
 
3473
 
 
3474
    const char *ImagesNodes[] = { "HardDisks", "DVDImages", "FloppyImages" };
 
3475
 
 
3476
    for (size_t node = 0; node < ELEMENTS (ImagesNodes) && SUCCEEDED (rc); node ++)
 
3477
    {
 
3478
        CFGNODE imagesNode = 0;
 
3479
        CFGLDRGetChildNode (registryNode, ImagesNodes [node], 0, &imagesNode);
 
3480
 
 
3481
        // all three nodes are optional
 
3482
        if (!imagesNode)
 
3483
            continue;
 
3484
 
 
3485
        if (node == 0) // HardDisks node
 
3486
            rc = loadHardDisks (imagesNode);
 
3487
        else
 
3488
        {
 
3489
            unsigned count = 0;
 
3490
            CFGLDRCountChildren (imagesNode, "Image", &count);
 
3491
            for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
 
3492
            {
 
3493
                CFGNODE imageNode = 0;
 
3494
                CFGLDRGetChildNode (imagesNode, "Image", i, &imageNode);
 
3495
                ComAssertBreak (imageNode, rc = E_FAIL);
 
3496
 
 
3497
                Guid uuid; // uuid (required)
 
3498
                CFGLDRQueryUUID (imageNode, "uuid", uuid.ptr());
 
3499
                Bstr src; // source (required)
 
3500
                CFGLDRQueryBSTR (imageNode, "src", src.asOutParam());
 
3501
 
 
3502
                switch (node)
 
3503
                {
 
3504
                    case 1: // DVDImages
 
3505
                    {
 
3506
                        ComObjPtr <DVDImage> dvdImage;
 
3507
                        dvdImage.createObject();
 
3508
                        rc = dvdImage->init (this, src, TRUE /* isRegistered */, uuid);
 
3509
                        if (SUCCEEDED (rc))
 
3510
                            rc = registerDVDImage (dvdImage, TRUE /* aOnStartUp */);
 
3511
 
 
3512
                        break;
 
3513
                    }
 
3514
                    case 2: // FloppyImages
 
3515
                    {
 
3516
                        ComObjPtr <FloppyImage> floppyImage;
 
3517
                        floppyImage.createObject();
 
3518
                        rc = floppyImage->init (this, src, TRUE /* isRegistered */, uuid);
 
3519
                        if (SUCCEEDED (rc))
 
3520
                            rc = registerFloppyImage (floppyImage, TRUE /* aOnStartUp */);
 
3521
 
 
3522
                        break;
 
3523
                    }
 
3524
                    default:
 
3525
                        AssertFailed();
 
3526
                }
 
3527
 
 
3528
                CFGLDRReleaseNode (imageNode);
 
3529
            }
 
3530
        }
 
3531
 
 
3532
        CFGLDRReleaseNode (imagesNode);
 
3533
    }
 
3534
 
 
3535
    CFGLDRReleaseNode (registryNode);
 
3536
 
 
3537
    return rc;
 
3538
}
 
3539
 
 
3540
/**
 
3541
 *  Loads all hard disks from the given <HardDisks> node.
 
3542
 *  Note that all loaded hard disks register themselves within this VirtualBox.
 
3543
 *
 
3544
 *  @param aNode        <HardDisks> node
 
3545
 *
 
3546
 *  @note Can be called only from #init().
 
3547
 *  @note Doesn't lock anything.
 
3548
 */
 
3549
HRESULT VirtualBox::loadHardDisks (CFGNODE aNode)
 
3550
{
 
3551
    AutoCaller autoCaller (this);
 
3552
    AssertReturn (autoCaller.state() == InInit, E_FAIL);
 
3553
 
 
3554
    AssertReturn (aNode, E_INVALIDARG);
 
3555
 
 
3556
    HRESULT rc = S_OK;
 
3557
 
 
3558
    unsigned count = 0;
 
3559
    CFGLDRCountChildren (aNode, "HardDisk", &count);
 
3560
    for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
 
3561
    {
 
3562
        CFGNODE hdNode = 0;
 
3563
 
 
3564
        CFGLDRGetChildNode (aNode, "HardDisk", i, &hdNode);
 
3565
        ComAssertBreak (hdNode, rc = E_FAIL);
 
3566
 
 
3567
        {
 
3568
            CFGNODE storageNode = 0;
 
3569
 
 
3570
            // detect the type of the hard disk
 
3571
            // (either one of HVirtualDiskImage, HISCSIHardDisk or HPhysicalVolume
 
3572
            do
 
3573
            {
 
3574
                CFGLDRGetChildNode (hdNode, "VirtualDiskImage", 0, &storageNode);
 
3575
                if (storageNode)
 
3576
                {
 
3577
                    ComObjPtr <HVirtualDiskImage> vdi;
 
3578
                    vdi.createObject();
 
3579
                    rc = vdi->init (this, NULL, hdNode, storageNode);
 
3580
                    break;
 
3581
                }
 
3582
 
 
3583
                CFGLDRGetChildNode (hdNode, "ISCSIHardDisk", 0, &storageNode);
 
3584
                if (storageNode)
 
3585
                {
 
3586
                    ComObjPtr <HISCSIHardDisk> iscsi;
 
3587
                    iscsi.createObject();
 
3588
                    rc = iscsi->init (this, hdNode, storageNode);
 
3589
                    break;
 
3590
                }
 
3591
 
 
3592
                CFGLDRGetChildNode (hdNode, "VMDKImage", 0, &storageNode);
 
3593
                if (storageNode)
 
3594
                {
 
3595
                    ComObjPtr <HVMDKImage> vmdk;
 
3596
                    vmdk.createObject();
 
3597
                    rc = vmdk->init (this, NULL, hdNode, storageNode);
 
3598
                    break;
 
3599
                }
 
3600
 
 
3601
                /// @todo (dmik) later
 
3602
//                CFGLDRGetChildNode (hdNode, "PhysicalVolume", 0, &storageNode);
 
3603
//                if (storageNode)
 
3604
//                {
 
3605
//                    break;
 
3606
//                }
 
3607
 
 
3608
                ComAssertMsgFailedBreak (("No valid hard disk storage node!\n"),
 
3609
                                         rc = E_FAIL);
 
3610
            }
 
3611
            while (0);
 
3612
 
 
3613
            if (storageNode)
 
3614
                CFGLDRReleaseNode (storageNode);
 
3615
        }
 
3616
 
 
3617
        CFGLDRReleaseNode (hdNode);
 
3618
    }
 
3619
 
 
3620
    return rc;
 
3621
}
 
3622
 
 
3623
/**
 
3624
 *  Helper function to write out the configuration to XML.
 
3625
 *
 
3626
 *  @note Locks objects!
 
3627
 */
 
3628
HRESULT VirtualBox::saveConfig()
 
3629
{
 
3630
    AutoCaller autoCaller (this);
 
3631
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
3632
 
 
3633
    ComAssertRet (!!mData.mCfgFile.mName, E_FAIL);
 
3634
 
 
3635
    HRESULT rc = S_OK;
 
3636
 
 
3637
    AutoLock alock (this);
 
3638
 
 
3639
    CFGHANDLE configLoader;
 
3640
 
 
3641
    /* load the config file */
 
3642
    int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
 
3643
                          mData.mCfgFile.mHandle,
 
3644
                          XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
 
3645
    ComAssertRCRet (vrc, E_FAIL);
 
3646
 
 
3647
    const char * Global = "VirtualBox/Global";
 
3648
    CFGNODE global;
 
3649
 
 
3650
    vrc = CFGLDRGetNode(configLoader, Global, 0, &global);
 
3651
    if (VBOX_FAILURE (vrc))
 
3652
        CFGLDRCreateNode (configLoader, Global, &global);
 
3653
 
 
3654
    do
 
3655
    {
 
3656
        ComAssertBreak (global, rc = E_FAIL);
 
3657
 
 
3658
        /* machines */
 
3659
        do
 
3660
        {
 
3661
            const char *Registry = "MachineRegistry";
 
3662
            const char *Entry = "MachineEntry";
 
3663
            CFGNODE registryNode = NULL;
 
3664
 
 
3665
            /* first, delete the entire machine registry */
 
3666
            if (VBOX_SUCCESS (CFGLDRGetChildNode (global, Registry, 0, &registryNode)))
 
3667
                CFGLDRDeleteNode (registryNode);
 
3668
 
 
3669
            /* then, recreate it */
 
3670
            CFGLDRCreateChildNode (global, Registry, &registryNode);
 
3671
 
 
3672
            /* write out the machines */
 
3673
            for (MachineList::iterator it = mData.mMachines.begin();
 
3674
                 it != mData.mMachines.end();
 
3675
                 ++ it)
 
3676
            {
 
3677
                /// @todo (dmik) move this part to Machine for better incapsulation
 
3678
 
 
3679
                ComObjPtr <Machine> m = *it;
 
3680
                AutoReaderLock machineLock (m);
 
3681
 
 
3682
                AssertMsg (m->data(), ("Machine data must not be NULL"));
 
3683
                CFGNODE entryNode;
 
3684
                CFGLDRAppendChildNode (registryNode, Entry, &entryNode);
 
3685
                /* UUID + curly brackets */
 
3686
                CFGLDRSetUUID (entryNode, "uuid", unconst (m->data()->mUuid).ptr());
 
3687
                /* source */
 
3688
                CFGLDRSetBSTR (entryNode, "src", m->data()->mConfigFile);
 
3689
                /* done */
 
3690
                CFGLDRReleaseNode (entryNode);
 
3691
            }
 
3692
 
 
3693
            CFGLDRReleaseNode (registryNode);
 
3694
        }
 
3695
        while (0);
 
3696
        if (FAILED (rc))
 
3697
            break;
 
3698
 
 
3699
        /* disk images */
 
3700
        do
 
3701
        {
 
3702
            CFGNODE registryNode = 0;
 
3703
            CFGLDRGetChildNode (global, "DiskRegistry", 0, &registryNode);
 
3704
            /* first, delete the entire disk image registr */
 
3705
            if (registryNode)
 
3706
                CFGLDRDeleteNode (registryNode);
 
3707
            /* then, recreate it */
 
3708
            CFGLDRCreateChildNode (global, "DiskRegistry", &registryNode);
 
3709
            ComAssertBreak (registryNode, rc = E_FAIL);
 
3710
 
 
3711
            /* write out the hard disks */
 
3712
            {
 
3713
                CFGNODE imagesNode = 0;
 
3714
                CFGLDRCreateChildNode (registryNode, "HardDisks", &imagesNode);
 
3715
                rc = saveHardDisks (imagesNode);
 
3716
                CFGLDRReleaseNode (imagesNode);
 
3717
                if (FAILED (rc))
 
3718
                    break;
 
3719
            }
 
3720
 
 
3721
            /* write out the CD/DVD images */
 
3722
            {
 
3723
                CFGNODE imagesNode = 0;
 
3724
                CFGLDRCreateChildNode (registryNode, "DVDImages", &imagesNode);
 
3725
 
 
3726
                for (DVDImageList::iterator it = mData.mDVDImages.begin();
 
3727
                     it != mData.mDVDImages.end();
 
3728
                     ++ it)
 
3729
                {
 
3730
                    ComObjPtr <DVDImage> dvd = *it;
 
3731
                    /* no need to lock: fields are constant */
 
3732
                    CFGNODE imageNode = 0;
 
3733
                    CFGLDRAppendChildNode (imagesNode, "Image", &imageNode);
 
3734
                    CFGLDRSetUUID (imageNode, "uuid", dvd->id());
 
3735
                    CFGLDRSetBSTR (imageNode, "src", dvd->filePath());
 
3736
                    CFGLDRReleaseNode (imageNode);
 
3737
                }
 
3738
 
 
3739
                CFGLDRReleaseNode (imagesNode);
 
3740
            }
 
3741
 
 
3742
            /* write out the floppy images */
 
3743
            {
 
3744
                CFGNODE imagesNode = 0;
 
3745
                CFGLDRCreateChildNode (registryNode, "FloppyImages", &imagesNode);
 
3746
 
 
3747
                for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
 
3748
                     it != mData.mFloppyImages.end();
 
3749
                     ++ it)
 
3750
                {
 
3751
                    ComObjPtr <FloppyImage> fd = *it;
 
3752
                    /* no need to lock: fields are constant */
 
3753
                    CFGNODE imageNode = 0;
 
3754
                    CFGLDRAppendChildNode (imagesNode, "Image", &imageNode);
 
3755
                    CFGLDRSetUUID (imageNode, "uuid", fd->id());
 
3756
                    CFGLDRSetBSTR (imageNode, "src", fd->filePath());
 
3757
                    CFGLDRReleaseNode (imageNode);
 
3758
                }
 
3759
 
 
3760
                CFGLDRReleaseNode (imagesNode);
 
3761
            }
 
3762
 
 
3763
            CFGLDRReleaseNode (registryNode);
 
3764
        }
 
3765
        while (0);
 
3766
        if (FAILED (rc))
 
3767
            break;
 
3768
 
 
3769
        do
 
3770
        {
 
3771
            /* host data (USB filters) */
 
3772
            rc = mData.mHost->saveSettings (global);
 
3773
            if (FAILED (rc))
 
3774
                break;
 
3775
 
 
3776
            rc = mData.mSystemProperties->saveSettings (global);
 
3777
            if (FAILED (rc))
 
3778
                break;
 
3779
        }
 
3780
        while (0);
 
3781
    }
 
3782
    while (0);
 
3783
 
 
3784
    if (global)
 
3785
        CFGLDRReleaseNode (global);
 
3786
 
 
3787
    if (SUCCEEDED (rc))
 
3788
    {
 
3789
        char *loaderError = NULL;
 
3790
        vrc = CFGLDRSave (configLoader, &loaderError);
 
3791
        if (VBOX_FAILURE (vrc))
 
3792
        {
 
3793
            rc = setError (E_FAIL,
 
3794
                tr ("Could not save the settings file '%ls' (%Vrc)%s%s"),
 
3795
                mData.mCfgFile.mName.raw(), vrc,
 
3796
                loaderError ? ".\n" : "", loaderError ? loaderError : "");
 
3797
            if (loaderError)
 
3798
                RTMemTmpFree (loaderError);
 
3799
        }
 
3800
    }
 
3801
 
 
3802
    CFGLDRFree(configLoader);
 
3803
 
 
3804
    return rc;
 
3805
}
 
3806
 
 
3807
/**
 
3808
 *  Saves all hard disks to the given <HardDisks> node.
 
3809
 *
 
3810
 *  @param aNode        <HardDisks> node
 
3811
 *
 
3812
 *  @note Locks this object for reding.
 
3813
 */
 
3814
HRESULT VirtualBox::saveHardDisks (CFGNODE aNode)
 
3815
{
 
3816
    AssertReturn (aNode, E_INVALIDARG);
 
3817
 
 
3818
    HRESULT rc = S_OK;
 
3819
 
 
3820
    AutoReaderLock alock (this);
 
3821
 
 
3822
    for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
 
3823
         it != mData.mHardDisks.end() && SUCCEEDED (rc);
 
3824
         ++ it)
 
3825
    {
 
3826
        ComObjPtr <HardDisk> hd = *it;
 
3827
        AutoReaderLock hdLock (hd);
 
3828
 
 
3829
        CFGNODE hdNode = 0;
 
3830
        CFGLDRAppendChildNode (aNode, "HardDisk", &hdNode);
 
3831
        ComAssertBreak (hdNode, rc = E_FAIL);
 
3832
 
 
3833
        CFGNODE storageNode = 0;
 
3834
 
 
3835
        switch (hd->storageType())
 
3836
        {
 
3837
            case HardDiskStorageType_VirtualDiskImage:
 
3838
            {
 
3839
                CFGLDRAppendChildNode (hdNode, "VirtualDiskImage", &storageNode);
 
3840
                ComAssertBreak (storageNode, rc = E_FAIL);
 
3841
                rc = hd->saveSettings (hdNode, storageNode);
 
3842
                break;
 
3843
            }
 
3844
 
 
3845
            case HardDiskStorageType_ISCSIHardDisk:
 
3846
            {
 
3847
                CFGLDRAppendChildNode (hdNode, "ISCSIHardDisk", &storageNode);
 
3848
                ComAssertBreak (storageNode, rc = E_FAIL);
 
3849
                rc = hd->saveSettings (hdNode, storageNode);
 
3850
                break;
 
3851
            }
 
3852
 
 
3853
            case HardDiskStorageType_VMDKImage:
 
3854
            {
 
3855
                CFGLDRAppendChildNode (hdNode, "VMDKImage", &storageNode);
 
3856
                ComAssertBreak (storageNode, rc = E_FAIL);
 
3857
                rc = hd->saveSettings (hdNode, storageNode);
 
3858
                break;
 
3859
            }
 
3860
 
 
3861
            /// @todo (dmik) later
 
3862
//            case HardDiskStorageType_PhysicalVolume:
 
3863
//            {
 
3864
//                break;
 
3865
//            }
 
3866
        }
 
3867
 
 
3868
        if (storageNode)
 
3869
            CFGLDRReleaseNode (storageNode);
 
3870
 
 
3871
        CFGLDRReleaseNode (hdNode);
 
3872
    }
 
3873
 
 
3874
    return rc;
 
3875
}
 
3876
 
 
3877
/**
 
3878
 *  Helper to register the machine.
 
3879
 *
 
3880
 *  When called during VirtualBox startup, adds the given machine to the
 
3881
 *  collection of registered machines. Otherwise tries to mark the machine
 
3882
 *  as registered, and, if succeeded, adds it to the collection and
 
3883
 *  saves global settings.
 
3884
 *
 
3885
 *  @param aMachine     machine to register
 
3886
 *
 
3887
 *  @note Locks objects!
 
3888
 */
 
3889
HRESULT VirtualBox::registerMachine (Machine *aMachine)
 
3890
{
 
3891
    ComAssertRet (aMachine, E_INVALIDARG);
 
3892
 
 
3893
    AutoCaller autoCaller (this);
 
3894
    CheckComRCReturnRC (autoCaller.rc());
 
3895
 
 
3896
    AutoLock alock (this);
 
3897
 
 
3898
    ComAssertRet (findMachine (aMachine->data()->mUuid,
 
3899
                  false /* aDoSetError  */, NULL) == E_INVALIDARG,
 
3900
                  E_FAIL);
 
3901
 
 
3902
    HRESULT rc = S_OK;
 
3903
 
 
3904
    if (autoCaller.state() != InInit)
 
3905
    {
 
3906
        /* Machine::trySetRegistered() will commit and save machine settings */
 
3907
        rc = aMachine->trySetRegistered (TRUE);
 
3908
        CheckComRCReturnRC (rc);
 
3909
    }
 
3910
 
 
3911
    /* add to the collection of registered machines */
 
3912
    mData.mMachines.push_back (aMachine);
 
3913
 
 
3914
    if (autoCaller.state() != InInit)
 
3915
        rc = saveConfig();
 
3916
 
 
3917
    return rc;
 
3918
}
 
3919
 
 
3920
/**
 
3921
 *  Helper to register the hard disk.
 
3922
 *
 
3923
 *  @param aHardDisk    object to register
 
3924
 *  @param aFlags       one of RHD_* values
 
3925
 *
 
3926
 *  @note Locks objects!
 
3927
 */
 
3928
HRESULT VirtualBox::registerHardDisk (HardDisk *aHardDisk, RHD_Flags aFlags)
 
3929
{
 
3930
    ComAssertRet (aHardDisk, E_INVALIDARG);
 
3931
 
 
3932
    AutoCaller autoCaller (this);
 
3933
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
3934
 
 
3935
    AutoLock alock (this);
 
3936
 
 
3937
    HRESULT rc = checkMediaForConflicts (aHardDisk, NULL, NULL);
 
3938
    CheckComRCReturnRC (rc);
 
3939
 
 
3940
    /* mark the hard disk as registered only when registration is external */
 
3941
    if (aFlags == RHD_External)
 
3942
    {
 
3943
        rc = aHardDisk->trySetRegistered (TRUE);
 
3944
        CheckComRCReturnRC (rc);
 
3945
    }
 
3946
 
 
3947
    if (!aHardDisk->parent())
 
3948
    {
 
3949
        /* add to the collection of top-level images */
 
3950
        mData.mHardDisks.push_back (aHardDisk);
 
3951
    }
 
3952
 
 
3953
    /* insert to the map of hard disks */
 
3954
    mData.mHardDiskMap
 
3955
        .insert (HardDiskMap::value_type (aHardDisk->id(), aHardDisk));
 
3956
 
 
3957
    /* save global config file if not on startup */
 
3958
    /// @todo (dmik) optimize later to save only the <HardDisks> node
 
3959
    if (aFlags != RHD_OnStartUp)
 
3960
        rc = saveConfig();
 
3961
 
 
3962
    return rc;
 
3963
}
 
3964
 
 
3965
/**
 
3966
 *  Helper to unregister the hard disk.
 
3967
 *
 
3968
 *  If the hard disk is a differencing hard disk and if the unregistration
 
3969
 *  succeeds, the hard disk image is deleted and the object is uninitialized.
 
3970
 *
 
3971
 *  @param aHardDisk    hard disk to unregister
 
3972
 *
 
3973
 *  @note Locks objects!
 
3974
 */
 
3975
HRESULT VirtualBox::unregisterHardDisk (HardDisk *aHardDisk)
 
3976
{
 
3977
    AssertReturn (aHardDisk, E_INVALIDARG);
 
3978
 
 
3979
    AutoCaller autoCaller (this);
 
3980
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
3981
 
 
3982
    LogFlowThisFunc (("image='%ls'\n", aHardDisk->toString().raw()));
 
3983
 
 
3984
    AutoLock alock (this);
 
3985
 
 
3986
    /* Lock the hard disk to ensure nobody registers it again before we delete
 
3987
     * the differencing image (sanity check actually -- should never happen). */
 
3988
    AutoLock hdLock (aHardDisk);
 
3989
 
 
3990
    /* try to unregister */
 
3991
    HRESULT rc = aHardDisk->trySetRegistered (FALSE);
 
3992
    CheckComRCReturnRC (rc);
 
3993
 
 
3994
    /* remove from the map of hard disks */
 
3995
    mData.mHardDiskMap.erase (aHardDisk->id());
 
3996
 
 
3997
    if (!aHardDisk->parent())
 
3998
    {
 
3999
        /* non-differencing hard disk:
 
4000
         * remove from the collection of top-level hard disks */
 
4001
        mData.mHardDisks.remove (aHardDisk);
 
4002
    }
 
4003
    else
 
4004
    {
 
4005
        Assert (aHardDisk->isDifferencing());
 
4006
 
 
4007
        /* differencing hard disk: delete (only if the last access check
 
4008
         * succeeded) and uninitialize */
 
4009
        if (aHardDisk->asVDI()->lastAccessError().isNull())
 
4010
            rc = aHardDisk->asVDI()->DeleteImage();
 
4011
        aHardDisk->uninit();
 
4012
    }
 
4013
 
 
4014
    /* save the global config file anyway (already unregistered) */
 
4015
    /// @todo (dmik) optimize later to save only the <HardDisks> node
 
4016
    HRESULT rc2 = saveConfig();
 
4017
    if (SUCCEEDED (rc))
 
4018
        rc = rc2;
 
4019
 
 
4020
    return rc;
 
4021
}
 
4022
 
 
4023
/**
 
4024
 *  Helper to unregister the differencing hard disk image.
 
4025
 *  Resets machine ID of the hard disk (to let the unregistration succeed)
 
4026
 *  and then calls #unregisterHardDisk().
 
4027
 *
 
4028
 *  @param aHardDisk    differencing hard disk image to unregister
 
4029
 *
 
4030
 *  @note Locks objects!
 
4031
 */
 
4032
HRESULT VirtualBox::unregisterDiffHardDisk (HardDisk *aHardDisk)
 
4033
{
 
4034
    AssertReturn (aHardDisk, E_INVALIDARG);
 
4035
 
 
4036
    AutoCaller autoCaller (this);
 
4037
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
4038
 
 
4039
    AutoLock alock (this);
 
4040
 
 
4041
    /*
 
4042
     *  Note: it's safe to lock aHardDisk here because the same object
 
4043
     *  will be locked by #unregisterHardDisk().
 
4044
     */
 
4045
    AutoLock hdLock (aHardDisk);
 
4046
 
 
4047
    AssertReturn (aHardDisk->isDifferencing(), E_INVALIDARG);
 
4048
 
 
4049
    /*
 
4050
     *  deassociate the machine from the hard disk
 
4051
     *  (otherwise trySetRegistered() will definitely fail)
 
4052
     */
 
4053
    aHardDisk->setMachineId (Guid());
 
4054
 
 
4055
    return unregisterHardDisk (aHardDisk);
 
4056
}
 
4057
 
 
4058
 
 
4059
/**
 
4060
 *  Helper to update the global settings file when the name of some machine
 
4061
 *  changes so that file and directory renaming occurs. This method ensures
 
4062
 *  that all affected paths in the disk registry are properly updated.
 
4063
 *
 
4064
 *  @param aOldPath old path (full)
 
4065
 *  @param aNewPath new path (full)
 
4066
 *
 
4067
 *  @note Locks this object + DVD, Floppy and HardDisk children for writing.
 
4068
 */
 
4069
HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
 
4070
{
 
4071
    LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
 
4072
 
 
4073
    AssertReturn (aOldPath, E_INVALIDARG);
 
4074
    AssertReturn (aNewPath, E_INVALIDARG);
 
4075
 
 
4076
    AutoCaller autoCaller (this);
 
4077
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
4078
 
 
4079
    AutoLock alock (this);
 
4080
 
 
4081
    size_t oldPathLen = strlen (aOldPath);
 
4082
 
 
4083
    /* check DVD paths */
 
4084
    for (DVDImageList::iterator it = mData.mDVDImages.begin();
 
4085
         it != mData.mDVDImages.end();
 
4086
         ++ it)
 
4087
    {
 
4088
        ComObjPtr <DVDImage> image = *it;
 
4089
 
 
4090
        /* no need to lock: fields are constant */
 
4091
        Utf8Str path = image->filePathFull();
 
4092
        LogFlowThisFunc (("DVD.fullPath={%s}\n", path.raw()));
 
4093
 
 
4094
        if (RTPathStartsWith (path, aOldPath))
 
4095
        {
 
4096
            Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
 
4097
                                          path.raw() + oldPathLen);
 
4098
            path = newPath;
 
4099
            calculateRelativePath (path, path);
 
4100
            image->updatePath (newPath, path);
 
4101
 
 
4102
            LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
 
4103
                              newPath.raw(), path.raw()));
 
4104
        }
 
4105
    }
 
4106
 
 
4107
    /* check Floppy paths */
 
4108
    for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
 
4109
         it != mData.mFloppyImages.end();
 
4110
         ++ it)
 
4111
    {
 
4112
        ComObjPtr <FloppyImage> image = *it;
 
4113
 
 
4114
        /* no need to lock: fields are constant */
 
4115
        Utf8Str path = image->filePathFull();
 
4116
        LogFlowThisFunc (("Floppy.fullPath={%s}\n", path.raw()));
 
4117
 
 
4118
        if (RTPathStartsWith (path, aOldPath))
 
4119
        {
 
4120
            Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
 
4121
                                          path.raw() + oldPathLen);
 
4122
            path = newPath;
 
4123
            calculateRelativePath (path, path);
 
4124
            image->updatePath (newPath, path);
 
4125
 
 
4126
            LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
 
4127
                              newPath.raw(), path.raw()));
 
4128
        }
 
4129
    }
 
4130
 
 
4131
    /* check HardDisk paths */
 
4132
    for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
 
4133
         it != mData.mHardDisks.end();
 
4134
         ++ it)
 
4135
    {
 
4136
        (*it)->updatePaths (aOldPath, aNewPath);
 
4137
    }
 
4138
 
 
4139
    HRESULT rc = saveConfig();
 
4140
 
 
4141
    return rc;
 
4142
}
 
4143
 
 
4144
/**
 
4145
 *  Helper to register the DVD image.
 
4146
 *
 
4147
 *  @param aImage       object to register
 
4148
 *  @param aOnStartUp   whether this method called during VirtualBox init or not
 
4149
 *
 
4150
 *  @return COM status code
 
4151
 *
 
4152
 *  @note Locks objects!
 
4153
 */
 
4154
HRESULT VirtualBox::registerDVDImage (DVDImage *aImage, bool aOnStartUp)
 
4155
{
 
4156
    AssertReturn (aImage, E_INVALIDARG);
 
4157
 
 
4158
    AutoCaller autoCaller (this);
 
4159
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
4160
 
 
4161
    AutoLock alock (this);
 
4162
 
 
4163
    HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
 
4164
                                         aImage->filePathFull());
 
4165
    CheckComRCReturnRC (rc);
 
4166
 
 
4167
    /* add to the collection */
 
4168
    mData.mDVDImages.push_back (aImage);
 
4169
 
 
4170
    /* save global config file if we're supposed to */
 
4171
    if (!aOnStartUp)
 
4172
        rc = saveConfig();
 
4173
 
 
4174
    return rc;
 
4175
}
 
4176
 
 
4177
/**
 
4178
 *  Helper to register the floppy image.
 
4179
 *
 
4180
 *  @param aImage       object to register
 
4181
 *  @param aOnStartUp   whether this method called during VirtualBox init or not
 
4182
 *
 
4183
 *  @return COM status code
 
4184
 *
 
4185
 *  @note Locks objects!
 
4186
 */
 
4187
HRESULT VirtualBox::registerFloppyImage (FloppyImage *aImage, bool aOnStartUp)
 
4188
{
 
4189
    AssertReturn (aImage, E_INVALIDARG);
 
4190
 
 
4191
    AutoCaller autoCaller (this);
 
4192
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
4193
 
 
4194
    AutoLock alock (this);
 
4195
 
 
4196
    HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
 
4197
                                         aImage->filePathFull());
 
4198
    CheckComRCReturnRC (rc);
 
4199
 
 
4200
    /* add to the collection */
 
4201
    mData.mFloppyImages.push_back (aImage);
 
4202
 
 
4203
    /* save global config file if we're supposed to */
 
4204
    if (!aOnStartUp)
 
4205
        rc = saveConfig();
 
4206
 
 
4207
    return rc;
 
4208
}
 
4209
 
 
4210
/**
 
4211
 * Helper function to create the guest OS type objects and our collection
 
4212
 *
 
4213
 * @returns COM status code
 
4214
 */
 
4215
HRESULT VirtualBox::registerGuestOSTypes()
 
4216
{
 
4217
    AutoCaller autoCaller (this);
 
4218
    AssertComRCReturn (autoCaller.rc(), E_FAIL);
 
4219
    AssertReturn (autoCaller.state() == InInit, E_FAIL);
 
4220
 
 
4221
    HRESULT rc = S_OK;
 
4222
 
 
4223
    // this table represents our os type / string mapping
 
4224
    static struct
 
4225
    {
 
4226
        const char    *id;          // utf-8
 
4227
        const char    *description; // utf-8
 
4228
        const OSType   osType;
 
4229
        const uint32_t recommendedRAM;
 
4230
        const uint32_t recommendedVRAM;
 
4231
        const uint32_t recommendedHDD;
 
4232
    } OSTypes[] =
 
4233
    {
 
4234
        /// @todo (dmik) get the list of OS types from the XML schema
 
4235
        /* NOTE1: we assume that unknown is always the first entry!
 
4236
         * NOTE2: please use powers of 2 when specifying the size of harddisks since
 
4237
         *        '2GB' looks better than '1.95GB' (= 2000MB) */
 
4238
        { "unknown",    tr ("Other/Unknown"),  OSTypeUnknown,     64,   4,  2 * _1K },
 
4239
        { "dos",        "DOS",                 OSTypeDOS,         32,   4,      512 },
 
4240
        { "win31",      "Windows 3.1",         OSTypeWin31,       32,   4,  1 * _1K },
 
4241
        { "win95",      "Windows 95",          OSTypeWin95,       64,   4,  2 * _1K },
 
4242
        { "win98",      "Windows 98",          OSTypeWin98,       64,   4,  2 * _1K },
 
4243
        { "winme",      "Windows Me",          OSTypeWinMe,       64,   4,  4 * _1K },
 
4244
        { "winnt4",     "Windows NT 4",        OSTypeWinNT4,     128,   4,  2 * _1K },
 
4245
        { "win2k",      "Windows 2000",        OSTypeWin2k,      168,   4,  4 * _1K },
 
4246
        { "winxp",      "Windows XP",          OSTypeWinXP,      192,   4, 10 * _1K },
 
4247
        { "win2k3",     "Windows Server 2003", OSTypeWin2k3,     256,   4, 20 * _1K },
 
4248
        { "winvista",   "Windows Vista",       OSTypeWinVista,   512,   4, 20 * _1K },
 
4249
        { "os2warp3",   "OS/2 Warp 3",         OSTypeOS2Warp3,    48,   4,  1 * _1K },
 
4250
        { "os2warp4",   "OS/2 Warp 4",         OSTypeOS2Warp4,    64,   4,  2 * _1K },
 
4251
        { "os2warp45",  "OS/2 Warp 4.5",       OSTypeOS2Warp45,   96,   4,  2 * _1K },
 
4252
        { "linux22",    "Linux 2.2",           OSTypeLinux22,     64,   4,  2 * _1K },
 
4253
        { "linux24",    "Linux 2.4",           OSTypeLinux24,    128,   4,  4 * _1K },
 
4254
        { "linux26",    "Linux 2.6",           OSTypeLinux26,    256,   4,  8 * _1K },
 
4255
        { "freebsd",    "FreeBSD",             OSTypeFreeBSD,     64,   4,  2 * _1K },
 
4256
        { "openbsd",    "OpenBSD",             OSTypeOpenBSD,     64,   4,  2 * _1K },
 
4257
        { "netbsd",     "NetBSD",              OSTypeNetBSD,      64,   4,  2 * _1K },
 
4258
        { "netware",    "Netware",             OSTypeNetware,    128,   4,  4 * _1K },
 
4259
        { "solaris",    "Solaris",             OSTypeSolaris,    128,   4,  8 * _1K },
 
4260
        { "l4",         "L4",                  OSTypeL4,          64,   4,  2 * _1K }
 
4261
    };
 
4262
 
 
4263
    for (uint32_t i = 0; i < ELEMENTS (OSTypes) && SUCCEEDED (rc); i++)
 
4264
    {
 
4265
        ComObjPtr <GuestOSType> guestOSTypeObj;
 
4266
        rc = guestOSTypeObj.createObject();
 
4267
        if (SUCCEEDED (rc))
 
4268
        {
 
4269
            rc = guestOSTypeObj->init (OSTypes[i].id,
 
4270
                                       OSTypes[i].description,
 
4271
                                       OSTypes[i].osType,
 
4272
                                       OSTypes[i].recommendedRAM,
 
4273
                                       OSTypes[i].recommendedVRAM,
 
4274
                                       OSTypes[i].recommendedHDD);
 
4275
            if (SUCCEEDED (rc))
 
4276
                mData.mGuestOSTypes.push_back (guestOSTypeObj);
 
4277
        }
 
4278
    }
 
4279
 
 
4280
    return rc;
 
4281
}
 
4282
 
 
4283
/**
 
4284
 *  Helper to lock the VirtualBox configuration for write access.
 
4285
 *
 
4286
 *  @note This method is not thread safe (must be called only from #init()
 
4287
 *  or #uninit()).
 
4288
 *
 
4289
 *  @note If the configuration file is not found, the method returns
 
4290
 *  S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
 
4291
 *  in some places to determine the (valid) situation when no config file
 
4292
 *  exists yet, and therefore a new one should be created from scatch.
 
4293
 */
 
4294
HRESULT VirtualBox::lockConfig()
 
4295
{
 
4296
    AutoCaller autoCaller (this);
 
4297
    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
 
4298
    AssertReturn (autoCaller.state() == InInit, E_FAIL);
 
4299
 
 
4300
    HRESULT rc = S_OK;
 
4301
 
 
4302
    Assert (!isConfigLocked());
 
4303
    if (!isConfigLocked())
 
4304
    {
 
4305
        /* open the associated config file */
 
4306
        int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
 
4307
                             Utf8Str (mData.mCfgFile.mName),
 
4308
                             RTFILE_O_READWRITE | RTFILE_O_OPEN |
 
4309
                             RTFILE_O_DENY_WRITE);
 
4310
        if (VBOX_FAILURE (vrc))
 
4311
        {
 
4312
            mData.mCfgFile.mHandle = NIL_RTFILE;
 
4313
 
 
4314
            /*
 
4315
             *  It is ok if the file is not found, it will be created by
 
4316
             *  init(). Otherwise return an error.
 
4317
             */
 
4318
            if (vrc != VERR_FILE_NOT_FOUND)
 
4319
                rc = setError (E_FAIL,
 
4320
                    tr ("Could not lock the settings file '%ls' (%Vrc)"),
 
4321
                    mData.mCfgFile.mName.raw(), vrc);
 
4322
        }
 
4323
 
 
4324
        LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
 
4325
                          mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
 
4326
    }
 
4327
 
 
4328
    return rc;
 
4329
}
 
4330
 
 
4331
/**
 
4332
 *  Helper to unlock the VirtualBox configuration from write access.
 
4333
 *
 
4334
 *  @note This method is not thread safe (must be called only from #init()
 
4335
 *  or #uninit()).
 
4336
 */
 
4337
HRESULT VirtualBox::unlockConfig()
 
4338
{
 
4339
    AutoCaller autoCaller (this);
 
4340
    AssertComRCReturn (autoCaller.rc(), E_FAIL);
 
4341
    AssertReturn (autoCaller.state() == InUninit, E_FAIL);
 
4342
 
 
4343
    HRESULT rc = S_OK;
 
4344
 
 
4345
    if (isConfigLocked())
 
4346
    {
 
4347
        RTFileFlush (mData.mCfgFile.mHandle);
 
4348
        RTFileClose (mData.mCfgFile.mHandle);
 
4349
        /** @todo flush the directory too. */
 
4350
        mData.mCfgFile.mHandle = NIL_RTFILE;
 
4351
        LogFlowThisFunc (("\n"));
 
4352
    }
 
4353
 
 
4354
    return rc;
 
4355
}
 
4356
 
 
4357
/**
 
4358
 *  Thread function that watches the termination of all client processes
 
4359
 *  that have opened sessions using IVirtualBox::OpenSession()
 
4360
 */
 
4361
// static
 
4362
DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD thread, void *pvUser)
 
4363
{
 
4364
    LogFlowFuncEnter();
 
4365
 
 
4366
    VirtualBox *that = (VirtualBox *) pvUser;
 
4367
    Assert (that);
 
4368
 
 
4369
    SessionMachineVector machines;
 
4370
    size_t cnt = 0;
 
4371
 
 
4372
#if defined(RT_OS_WINDOWS)
 
4373
 
 
4374
    HRESULT hrc = CoInitializeEx (NULL,
 
4375
                                  COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
 
4376
                                  COINIT_SPEED_OVER_MEMORY);
 
4377
    AssertComRC (hrc);
 
4378
 
 
4379
    /// @todo (dmik) processes reaping!
 
4380
 
 
4381
    HANDLE *handles = new HANDLE [1];
 
4382
    handles [0] = that->mWatcherData.mUpdateReq;
 
4383
 
 
4384
    do
 
4385
    {
 
4386
        AutoCaller autoCaller (that);
 
4387
        /* VirtualBox has been early uninitialized, terminate */
 
4388
        if (!autoCaller.isOk())
 
4389
            break;
 
4390
 
 
4391
        do
 
4392
        {
 
4393
            /* release the caller to let uninit() ever proceed */
 
4394
            autoCaller.release();
 
4395
 
 
4396
            DWORD rc = ::WaitForMultipleObjects (cnt + 1, handles, FALSE, INFINITE);
 
4397
 
 
4398
            /*
 
4399
             *  Restore the caller before using VirtualBox. If it fails, this
 
4400
             *  means VirtualBox is being uninitialized and we must terminate.
 
4401
             */
 
4402
            autoCaller.add();
 
4403
            if (!autoCaller.isOk())
 
4404
                break;
 
4405
 
 
4406
            bool update = false;
 
4407
            if (rc == WAIT_OBJECT_0)
 
4408
            {
 
4409
                /* update event is signaled */
 
4410
                update = true;
 
4411
            }
 
4412
            else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
 
4413
            {
 
4414
                /* machine mutex is released */
 
4415
                (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
 
4416
                update = true;
 
4417
            }
 
4418
            else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
 
4419
            {
 
4420
                /* machine mutex is abandoned due to client process termination */
 
4421
                (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
 
4422
                update = true;
 
4423
            }
 
4424
            if (update)
 
4425
            {
 
4426
                /* obtain a new set of opened machines */
 
4427
                that->getOpenedMachines (machines);
 
4428
                cnt = machines.size();
 
4429
                LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
 
4430
                AssertMsg ((cnt + 1) <= MAXIMUM_WAIT_OBJECTS,
 
4431
                           ("MAXIMUM_WAIT_OBJECTS reached"));
 
4432
                /* renew the set of event handles */
 
4433
                delete [] handles;
 
4434
                handles = new HANDLE [cnt + 1];
 
4435
                handles [0] = that->mWatcherData.mUpdateReq;
 
4436
                for (size_t i = 0; i < cnt; ++ i)
 
4437
                    handles [i + 1] = (machines [i])->ipcSem();
 
4438
            }
 
4439
        }
 
4440
        while (true);
 
4441
    }
 
4442
    while (0);
 
4443
 
 
4444
    /* delete the set of event handles */
 
4445
    delete [] handles;
 
4446
 
 
4447
    /* delete the set of opened machines if any */
 
4448
    machines.clear();
 
4449
 
 
4450
    ::CoUninitialize();
 
4451
 
 
4452
#elif defined (RT_OS_OS2)
 
4453
 
 
4454
    /// @todo (dmik) processes reaping!
 
4455
 
 
4456
    /* according to PMREF, 64 is the maximum for the muxwait list */
 
4457
    SEMRECORD handles [64];
 
4458
 
 
4459
    HMUX muxSem = NULLHANDLE;
 
4460
 
 
4461
    do
 
4462
    {
 
4463
        AutoCaller autoCaller (that);
 
4464
        /* VirtualBox has been early uninitialized, terminate */
 
4465
        if (!autoCaller.isOk())
 
4466
            break;
 
4467
 
 
4468
        do
 
4469
        {
 
4470
            /* release the caller to let uninit() ever proceed */
 
4471
            autoCaller.release();
 
4472
 
 
4473
            int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
 
4474
 
 
4475
            /*
 
4476
             *  Restore the caller before using VirtualBox. If it fails, this
 
4477
             *  means VirtualBox is being uninitialized and we must terminate.
 
4478
             */
 
4479
            autoCaller.add();
 
4480
            if (!autoCaller.isOk())
 
4481
                break;
 
4482
 
 
4483
            bool update = false;
 
4484
 
 
4485
            if (VBOX_SUCCESS (vrc))
 
4486
            {
 
4487
                /* update event is signaled */
 
4488
                update = true;
 
4489
            }
 
4490
            else
 
4491
            {
 
4492
                AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
 
4493
                           ("RTSemEventWait returned %Vrc\n", vrc));
 
4494
 
 
4495
                /* are there any mutexes? */
 
4496
                if (cnt > 0)
 
4497
                {
 
4498
                    /* figure out what's going on with machines */
 
4499
 
 
4500
                    unsigned long semId = 0;
 
4501
                    APIRET arc = ::DosWaitMuxWaitSem (muxSem,
 
4502
                                                      SEM_IMMEDIATE_RETURN, &semId);
 
4503
 
 
4504
                    if (arc == NO_ERROR)
 
4505
                    {
 
4506
                        /* machine mutex is normally released */
 
4507
                        Assert (semId >= 0 && semId < cnt);
 
4508
                        if (semId >= 0 && semId < cnt)
 
4509
                            machines [semId]->checkForDeath();
 
4510
                        update = true;
 
4511
                    }
 
4512
                    else if (arc == ERROR_SEM_OWNER_DIED)
 
4513
                    {
 
4514
                        /* machine mutex is abandoned due to client process
 
4515
                         * termination; find which mutex is in the Owner Died
 
4516
                         * state */
 
4517
                        for (size_t i = 0; i < cnt; ++ i)
 
4518
                        {
 
4519
                            PID pid; TID tid;
 
4520
                            unsigned long reqCnt;
 
4521
                            arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
 
4522
                                                    &tid, &reqCnt);
 
4523
                            if (arc == ERROR_SEM_OWNER_DIED)
 
4524
                            {
 
4525
                                /* close the dead mutex as asked by PMREF */
 
4526
                                ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
 
4527
 
 
4528
                                Assert (i >= 0 && i < cnt);
 
4529
                                if (i >= 0 && i < cnt)
 
4530
                                    machines [i]->checkForDeath();
 
4531
                            }
 
4532
                        }
 
4533
                        update = true;
 
4534
                    }
 
4535
                    else
 
4536
                        AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
 
4537
                                   ("DosWaitMuxWaitSem returned %d\n", arc));
 
4538
                }
 
4539
            }
 
4540
 
 
4541
            if (update)
 
4542
            {
 
4543
                /* close the old muxsem */
 
4544
                if (muxSem != NULLHANDLE)
 
4545
                    ::DosCloseMuxWaitSem (muxSem);
 
4546
                /* obtain a new set of opened machines */
 
4547
                that->getOpenedMachines (machines);
 
4548
                cnt = machines.size();
 
4549
                LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
 
4550
                /// @todo use several muxwait sems if cnt is > 64
 
4551
                AssertMsg (cnt <= 64 /* according to PMREF */,
 
4552
                           ("maximum of 64 mutex semaphores reached (%d)", cnt));
 
4553
                if (cnt > 64)
 
4554
                    cnt = 64;
 
4555
                if (cnt > 0)
 
4556
                {
 
4557
                    /* renew the set of event handles */
 
4558
                    for (size_t i = 0; i < cnt; ++ i)
 
4559
                    {
 
4560
                        handles [i].hsemCur = (HSEM) machines [i]->ipcSem();
 
4561
                        handles [i].ulUser = i;
 
4562
                    }
 
4563
                    /* create a new muxsem */
 
4564
                    APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt, handles,
 
4565
                                                        DCMW_WAIT_ANY);
 
4566
                    AssertMsg (arc == NO_ERROR,
 
4567
                               ("DosCreateMuxWaitSem returned %d\n", arc));
 
4568
                }
 
4569
            }
 
4570
        }
 
4571
        while (true);
 
4572
    }
 
4573
    while (0);
 
4574
 
 
4575
    /* close the muxsem */
 
4576
    if (muxSem != NULLHANDLE)
 
4577
        ::DosCloseMuxWaitSem (muxSem);
 
4578
 
 
4579
    /* delete the set of opened machines if any */
 
4580
    machines.clear();
 
4581
 
 
4582
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
 
4583
 
 
4584
    bool need_update = false;
 
4585
 
 
4586
    do
 
4587
    {
 
4588
        AutoCaller autoCaller (that);
 
4589
        if (!autoCaller.isOk())
 
4590
            break;
 
4591
 
 
4592
        do
 
4593
        {
 
4594
            /* release the caller to let uninit() ever proceed */
 
4595
            autoCaller.release();
 
4596
 
 
4597
            int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
 
4598
 
 
4599
            /*
 
4600
             *  Restore the caller before using VirtualBox. If it fails, this
 
4601
             *  means VirtualBox is being uninitialized and we must terminate.
 
4602
             */
 
4603
            autoCaller.add();
 
4604
            if (!autoCaller.isOk())
 
4605
                break;
 
4606
 
 
4607
            if (VBOX_SUCCESS (rc) || need_update)
 
4608
            {
 
4609
                /* VBOX_SUCCESS (rc) means an update event is signaled */
 
4610
 
 
4611
                /* obtain a new set of opened machines */
 
4612
                that->getOpenedMachines (machines);
 
4613
                cnt = machines.size();
 
4614
                LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
 
4615
            }
 
4616
 
 
4617
            need_update = false;
 
4618
            for (size_t i = 0; i < cnt; ++ i)
 
4619
                need_update |= (machines [i])->checkForDeath();
 
4620
 
 
4621
            /* reap child processes */
 
4622
            {
 
4623
                AutoLock alock (that);
 
4624
                if (that->mWatcherData.mProcesses.size())
 
4625
                {
 
4626
                    LogFlowFunc (("UPDATE: child process count = %d\n",
 
4627
                                  that->mWatcherData.mProcesses.size()));
 
4628
                    ClientWatcherData::ProcessList::iterator it =
 
4629
                        that->mWatcherData.mProcesses.begin();
 
4630
                    while (it != that->mWatcherData.mProcesses.end())
 
4631
                    {
 
4632
                        RTPROCESS pid = *it;
 
4633
                        RTPROCSTATUS status;
 
4634
                        int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
 
4635
                                                &status);
 
4636
                        if (vrc == VINF_SUCCESS)
 
4637
                        {
 
4638
                            LogFlowFunc (("pid %d (%x) was reaped, "
 
4639
                                          "status=%d, reason=%d\n",
 
4640
                                          pid, pid, status.iStatus,
 
4641
                                          status.enmReason));
 
4642
                            it = that->mWatcherData.mProcesses.erase (it);
 
4643
                        }
 
4644
                        else
 
4645
                        {
 
4646
                            LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Vrc\n",
 
4647
                                          pid, pid, vrc));
 
4648
                            if (vrc != VERR_PROCESS_RUNNING)
 
4649
                            {
 
4650
                                /* remove the process if it is not already running */
 
4651
                                it = that->mWatcherData.mProcesses.erase (it);
 
4652
                            }
 
4653
                            else
 
4654
                                ++ it;
 
4655
                        }
 
4656
                    }
 
4657
                }
 
4658
            }
 
4659
        }
 
4660
        while (true);
 
4661
    }
 
4662
    while (0);
 
4663
 
 
4664
    /* delete the set of opened machines if any */
 
4665
    machines.clear();
 
4666
 
 
4667
#else
 
4668
# error "Port me!"
 
4669
#endif
 
4670
 
 
4671
    LogFlowFuncLeave();
 
4672
    return 0;
 
4673
}
 
4674
 
 
4675
/**
 
4676
 *  Thread function that handles custom events posted using #postEvent().
 
4677
 */
 
4678
// static
 
4679
DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
 
4680
{
 
4681
    LogFlowFuncEnter();
 
4682
 
 
4683
    AssertReturn (pvUser, VERR_INVALID_POINTER);
 
4684
 
 
4685
    // create an event queue for the current thread
 
4686
    EventQueue *eventQ = new EventQueue();
 
4687
    AssertReturn (eventQ, VERR_NO_MEMORY);
 
4688
 
 
4689
    // return the queue to the one who created this thread
 
4690
    *(static_cast <EventQueue **> (pvUser)) = eventQ;
 
4691
    // signal that we're ready
 
4692
    RTThreadUserSignal (thread);
 
4693
 
 
4694
    BOOL ok = TRUE;
 
4695
    Event *event = NULL;
 
4696
 
 
4697
    while ((ok = eventQ->waitForEvent (&event)) && event)
 
4698
        eventQ->handleEvent (event);
 
4699
 
 
4700
    AssertReturn (ok, VERR_GENERAL_FAILURE);
 
4701
 
 
4702
    delete eventQ;
 
4703
 
 
4704
    LogFlowFuncLeave();
 
4705
 
 
4706
    return 0;
 
4707
}
 
4708
 
 
4709
////////////////////////////////////////////////////////////////////////////////
 
4710
 
 
4711
/**
 
4712
 *  Takes the current list of registered callbacks of the managed VirtualBox
 
4713
 *  instance, and calls #handleCallback() for every callback item from the
 
4714
 *  list, passing the item as an argument.
 
4715
 *
 
4716
 *  @note Locks the managed VirtualBox object for reading but leaves the lock
 
4717
 *        before iterating over callbacks and calling their methods.
 
4718
 */
 
4719
void *VirtualBox::CallbackEvent::handler()
 
4720
{
 
4721
    if (mVirtualBox.isNull())
 
4722
        return NULL;
 
4723
 
 
4724
    AutoCaller autoCaller (mVirtualBox);
 
4725
    if (!autoCaller.isOk())
 
4726
    {
 
4727
        LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
 
4728
                         "the callback event is discarded!\n",
 
4729
                         autoCaller.state()));
 
4730
        /* We don't need mVirtualBox any more, so release it */
 
4731
        mVirtualBox.setNull();
 
4732
        return NULL;
 
4733
    }
 
4734
 
 
4735
    CallbackVector callbacks;
 
4736
    {
 
4737
        /* Make a copy to release the lock before iterating */
 
4738
        AutoReaderLock alock (mVirtualBox);
 
4739
        callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
 
4740
                                    mVirtualBox->mData.mCallbacks.end());
 
4741
        /* We don't need mVirtualBox any more, so release it */
 
4742
        mVirtualBox.setNull();
 
4743
    }
 
4744
 
 
4745
    for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
 
4746
         it != callbacks.end(); ++ it)
 
4747
        handleCallback (*it);
 
4748
 
 
4749
    return NULL;
 
4750
}
 
4751