VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageStorageController.cpp@ 37608

Last change on this file since 37608 was 37525, checked in by vboxsync, 14 years ago

Main/VirtualBox+Medium: resurrect the feature of changing the medium UUID when opening the image, which allows to resolve duplicate UUIDs without using external tools. Also fixes Medium::setIDs, which wasn't correctly working.
Frontends/*: corresponding changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.0 KB
Line 
1/* $Id: VBoxManageStorageController.cpp 37525 2011-06-17 10:09:21Z vboxsync $ */
2/** @file
3 * VBoxManage - The storage controller related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifndef VBOX_ONLY_DOCS
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include <VBox/com/com.h>
24#include <VBox/com/array.h>
25#include <VBox/com/ErrorInfo.h>
26#include <VBox/com/errorprint.h>
27#include <VBox/com/VirtualBox.h>
28
29#include <iprt/path.h>
30#include <iprt/param.h>
31#include <iprt/string.h>
32#include <iprt/ctype.h>
33#include <iprt/stream.h>
34#include <iprt/getopt.h>
35#include <VBox/log.h>
36
37#include "VBoxManage.h"
38using namespace com;
39
40
41// funcs
42///////////////////////////////////////////////////////////////////////////////
43
44
45static const RTGETOPTDEF g_aStorageAttachOptions[] =
46{
47 { "--storagectl", 's', RTGETOPT_REQ_STRING },
48 { "--port", 'p', RTGETOPT_REQ_UINT32 },
49 { "--device", 'd', RTGETOPT_REQ_UINT32 },
50 { "--type", 't', RTGETOPT_REQ_STRING },
51 { "--medium", 'm', RTGETOPT_REQ_STRING },
52 { "--mtype", 'M', RTGETOPT_REQ_STRING },
53 { "--passthrough", 'h', RTGETOPT_REQ_STRING },
54 { "--bandwidthgroup", 'b', RTGETOPT_REQ_STRING },
55 { "--forceunmount", 'f', RTGETOPT_REQ_NOTHING },
56 { "--comment", 'C', RTGETOPT_REQ_STRING },
57 { "--setuuid", 'q', RTGETOPT_REQ_STRING },
58 { "--setparentuuid", 'Q', RTGETOPT_REQ_STRING },
59 // iSCSI options
60 { "--server", 'S', RTGETOPT_REQ_STRING },
61 { "--target", 'T', RTGETOPT_REQ_STRING },
62 { "--port", 'P', RTGETOPT_REQ_STRING },
63 { "--lun", 'L', RTGETOPT_REQ_STRING },
64 { "--encodedlun", 'E', RTGETOPT_REQ_STRING },
65 { "--username", 'U', RTGETOPT_REQ_STRING },
66 { "--password", 'W', RTGETOPT_REQ_STRING },
67 { "--intnet", 'I', RTGETOPT_REQ_NOTHING },
68};
69
70int handleStorageAttach(HandlerArg *a)
71{
72 int c = VERR_INTERNAL_ERROR; /* initialized to shut up gcc */
73 HRESULT rc = S_OK;
74 ULONG port = ~0U;
75 ULONG device = ~0U;
76 bool fForceUnmount = false;
77 bool fSetMediumType = false;
78 bool fSetNewUuid = false;
79 bool fSetNewParentUuid = false;
80 MediumType_T mediumType = MediumType_Normal;
81 Bstr bstrComment;
82 const char *pszCtl = NULL;
83 DeviceType_T devTypeRequested = DeviceType_Null;
84 const char *pszMedium = NULL;
85 const char *pszPassThrough = NULL;
86 const char *pszBandwidthGroup = NULL;
87 Bstr bstrNewUuid;
88 Bstr bstrNewParentUuid;
89 // iSCSI options
90 Bstr bstrServer;
91 Bstr bstrTarget;
92 Bstr bstrPort;
93 Bstr bstrLun;
94 Bstr bstrUsername;
95 Bstr bstrPassword;
96 bool fIntNet = false;
97
98 RTGETOPTUNION ValueUnion;
99 RTGETOPTSTATE GetState;
100 ComPtr<IMachine> machine;
101 ComPtr<IStorageController> storageCtl;
102 ComPtr<ISystemProperties> systemProperties;
103
104 RTGetOptInit(&GetState, a->argc, a->argv, g_aStorageAttachOptions,
105 RT_ELEMENTS(g_aStorageAttachOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
106
107 while ( SUCCEEDED(rc)
108 && (c = RTGetOpt(&GetState, &ValueUnion)))
109 {
110 switch (c)
111 {
112 case 's': // storage controller name
113 {
114 if (ValueUnion.psz)
115 pszCtl = ValueUnion.psz;
116 else
117 rc = E_FAIL;
118 break;
119 }
120
121 case 'p': // port
122 {
123 port = ValueUnion.u32;
124 break;
125 }
126
127 case 'd': // device
128 {
129 device = ValueUnion.u32;
130 break;
131 }
132
133 case 'm': // medium <none|emptydrive|uuid|filename|host:<drive>|iSCSI>
134 {
135 if (ValueUnion.psz)
136 pszMedium = ValueUnion.psz;
137 else
138 rc = E_FAIL;
139 break;
140 }
141
142 case 't': // type <dvddrive|hdd|fdd>
143 {
144 if (ValueUnion.psz)
145 {
146 if (!RTStrICmp(ValueUnion.psz, "hdd"))
147 devTypeRequested = DeviceType_HardDisk;
148 else if (!RTStrICmp(ValueUnion.psz, "fdd"))
149 devTypeRequested = DeviceType_Floppy;
150 else if (!RTStrICmp(ValueUnion.psz, "dvddrive"))
151 devTypeRequested = DeviceType_DVD;
152 else
153 return errorArgument("Invalid --type argument '%s'", ValueUnion.psz);
154 }
155 else
156 rc = E_FAIL;
157 break;
158 }
159
160 case 'h': // passthrough <on|off>
161 {
162 if (ValueUnion.psz)
163 pszPassThrough = ValueUnion.psz;
164 else
165 rc = E_FAIL;
166 break;
167 }
168
169 case 'b': // bandwidthgroup <name>
170 {
171 if (ValueUnion.psz)
172 pszBandwidthGroup = ValueUnion.psz;
173 else
174 rc = E_FAIL;
175 break;
176 }
177
178 case 'f': // force unmount medium during runtime
179 {
180 fForceUnmount = true;
181 break;
182 }
183
184 case 'C':
185 if (ValueUnion.psz)
186 bstrComment = ValueUnion.psz;
187 else
188 rc = E_FAIL;
189 break;
190
191 case 'q':
192 if (ValueUnion.psz)
193 {
194 bstrNewUuid = ValueUnion.psz;
195 fSetNewUuid = true;
196 }
197 else
198 rc = E_FAIL;
199 break;
200
201 case 'Q':
202 if (ValueUnion.psz)
203 {
204 bstrNewParentUuid = ValueUnion.psz;
205 fSetNewParentUuid = true;
206 }
207 else
208 rc = E_FAIL;
209 break;
210
211 case 'S': // --server
212 bstrServer = ValueUnion.psz;
213 break;
214
215 case 'T': // --target
216 bstrTarget = ValueUnion.psz;
217 break;
218
219 case 'P': // --port
220 bstrPort = ValueUnion.psz;
221 break;
222
223 case 'L': // --lun
224 bstrLun = ValueUnion.psz;
225 break;
226
227 case 'E': // --encodedlun
228 bstrLun = BstrFmt("enc%s", ValueUnion.psz);
229 break;
230
231 case 'U': // --username
232 bstrUsername = ValueUnion.psz;
233 break;
234
235 case 'W': // --password
236 bstrPassword = ValueUnion.psz;
237 break;
238
239 case 'M': // --type
240 {
241 int vrc = parseDiskType(ValueUnion.psz, &mediumType);
242 if (RT_FAILURE(vrc))
243 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
244 fSetMediumType = true;
245 break;
246 }
247
248 case 'I': // --intnet
249 fIntNet = true;
250 break;
251
252 default:
253 {
254 errorGetOpt(USAGE_STORAGEATTACH, c, &ValueUnion);
255 rc = E_FAIL;
256 break;
257 }
258 }
259 }
260
261 if (FAILED(rc))
262 return 1;
263
264 if (!pszCtl)
265 return errorSyntax(USAGE_STORAGEATTACH, "Storage controller name not specified");
266 if (port == ~0U)
267 return errorSyntax(USAGE_STORAGEATTACH, "Port not specified");
268 if (device == ~0U)
269 return errorSyntax(USAGE_STORAGEATTACH, "Device not specified");
270
271 /* get the virtualbox system properties */
272 CHECK_ERROR_RET(a->virtualBox, COMGETTER(SystemProperties)(systemProperties.asOutParam()), 1);
273
274 // find the machine, lock it, get the mutable session machine
275 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
276 machine.asOutParam()), 1);
277 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);
278 SessionType_T st;
279 CHECK_ERROR_RET(a->session, COMGETTER(Type)(&st), 1);
280 a->session->COMGETTER(Machine)(machine.asOutParam());
281
282 try
283 {
284 bool fRunTime = (st == SessionType_Shared);
285 if (fRunTime)
286 {
287 if (devTypeRequested == DeviceType_HardDisk)
288 throw Utf8Str("Hard disk drives cannot be changed while the VM is running\n");
289 else if (!RTStrICmp(pszMedium, "none"))
290 throw Utf8Str("Drives cannot be removed while the VM is running\n");
291 else if (pszPassThrough)
292 throw Utf8Str("Drive passthrough state cannot be changed while the VM is running\n");
293 else if (pszBandwidthGroup)
294 throw Utf8Str("Bandwidth group cannot be changed while the VM is running\n");
295 }
296
297 /* check if the storage controller is present */
298 rc = machine->GetStorageControllerByName(Bstr(pszCtl).raw(),
299 storageCtl.asOutParam());
300 if (FAILED(rc))
301 throw Utf8StrFmt("Could not find a controller named '%s'\n", pszCtl);
302
303 /* for sata controller check if the port count is big enough
304 * to accommodate the current port which is being assigned
305 * else just increase the port count
306 */
307 {
308 ULONG ulPortCount = 0;
309 ULONG ulMaxPortCount = 0;
310
311 CHECK_ERROR(storageCtl, COMGETTER(MaxPortCount)(&ulMaxPortCount));
312 CHECK_ERROR(storageCtl, COMGETTER(PortCount)(&ulPortCount));
313
314 if ( (ulPortCount != ulMaxPortCount)
315 && (port >= ulPortCount)
316 && (port < ulMaxPortCount))
317 CHECK_ERROR(storageCtl, COMSETTER(PortCount)(port + 1));
318 }
319
320 StorageControllerType_T ctlType = StorageControllerType_Null;
321 CHECK_ERROR(storageCtl, COMGETTER(ControllerType)(&ctlType));
322
323 if (!RTStrICmp(pszMedium, "none"))
324 {
325 CHECK_ERROR(machine, DetachDevice(Bstr(pszCtl).raw(), port, device));
326 }
327 else if (!RTStrICmp(pszMedium, "emptydrive"))
328 {
329 if (fRunTime)
330 {
331 ComPtr<IMediumAttachment> mediumAttachment;
332 DeviceType_T deviceType = DeviceType_Null;
333 rc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port, device,
334 mediumAttachment.asOutParam());
335 if (SUCCEEDED(rc))
336 {
337 mediumAttachment->COMGETTER(Type)(&deviceType);
338
339 if ( (deviceType == DeviceType_DVD)
340 || (deviceType == DeviceType_Floppy))
341 {
342 /* just unmount the floppy/dvd */
343 CHECK_ERROR(machine, MountMedium(Bstr(pszCtl).raw(),
344 port,
345 device,
346 NULL,
347 fForceUnmount));
348 }
349 }
350
351 if ( FAILED(rc)
352 || !( deviceType == DeviceType_DVD
353 || deviceType == DeviceType_Floppy)
354 )
355 throw Utf8StrFmt("No DVD/Floppy Drive attached to the controller '%s'"
356 "at the port: %u, device: %u", pszCtl, port, device);
357
358 }
359 else
360 {
361 StorageBus_T storageBus = StorageBus_Null;
362 DeviceType_T deviceType = DeviceType_Null;
363 com::SafeArray <DeviceType_T> saDeviceTypes;
364 ULONG driveCheck = 0;
365
366 /* check if the device type is supported by the controller */
367 CHECK_ERROR(storageCtl, COMGETTER(Bus)(&storageBus));
368 CHECK_ERROR(systemProperties, GetDeviceTypesForStorageBus(storageBus, ComSafeArrayAsOutParam(saDeviceTypes)));
369 for (size_t i = 0; i < saDeviceTypes.size(); ++ i)
370 {
371 if ( (saDeviceTypes[i] == DeviceType_DVD)
372 || (saDeviceTypes[i] == DeviceType_Floppy))
373 driveCheck++;
374 }
375
376 if (!driveCheck)
377 throw Utf8StrFmt("The attachment is not supported by the storage controller '%s'", pszCtl);
378
379 if (storageBus == StorageBus_Floppy)
380 deviceType = DeviceType_Floppy;
381 else
382 deviceType = DeviceType_DVD;
383
384 /* attach a empty floppy/dvd drive after removing previous attachment */
385 machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
386 CHECK_ERROR(machine, AttachDevice(Bstr(pszCtl).raw(), port, device,
387 deviceType, NULL));
388 }
389 } // end if (!RTStrICmp(pszMedium, "emptydrive"))
390 else
391 {
392 ComPtr<IMedium> pMedium2Mount;
393
394 // not "none", not "emptydrive": then it must be a UUID or filename or hostdrive or iSCSI;
395 // for all these we first need to know the type of drive we're attaching to
396 {
397 /*
398 * try to determine the type of the drive from the
399 * storage controller chipset, the attachment and
400 * the medium being attached
401 */
402 if (ctlType == StorageControllerType_I82078) // floppy controller
403 devTypeRequested = DeviceType_Floppy;
404 else if (pszMedium)
405 {
406 /*
407 * for SATA/SCSI/IDE it is hard to tell if it is a harddisk or
408 * a dvd being attached so lets check if the medium attachment
409 * and the medium, both are of same type. if yes then we are
410 * sure of its type and don't need the user to enter it manually
411 * else ask the user for the type.
412 */
413 ComPtr<IMediumAttachment> mediumAttachment;
414 rc = machine->GetMediumAttachment(Bstr(pszCtl).raw(), port,
415 device,
416 mediumAttachment.asOutParam());
417 if (SUCCEEDED(rc))
418 {
419 DeviceType_T deviceType;
420 mediumAttachment->COMGETTER(Type)(&deviceType);
421
422 ComPtr<IMedium> pExistingMedium;
423 rc = findMedium(a, pszMedium, deviceType, true /* fSilent */,
424 pExistingMedium);
425 if (SUCCEEDED(rc) && pExistingMedium)
426 {
427 if ( (deviceType == DeviceType_DVD)
428 || (deviceType == DeviceType_HardDisk)
429 )
430 devTypeRequested = deviceType;
431 }
432 }
433 }
434 /* for all other cases lets ask the user what type of drive it is */
435 }
436
437 if (devTypeRequested == DeviceType_Null) // still the initializer value?
438 throw Utf8Str("Argument --type must be specified\n");
439
440 /* check if the device type is supported by the controller */
441 {
442 StorageBus_T storageBus = StorageBus_Null;
443 com::SafeArray <DeviceType_T> saDeviceTypes;
444
445 CHECK_ERROR(storageCtl, COMGETTER(Bus)(&storageBus));
446 CHECK_ERROR(systemProperties, GetDeviceTypesForStorageBus(storageBus, ComSafeArrayAsOutParam(saDeviceTypes)));
447 if (SUCCEEDED(rc))
448 {
449 ULONG driveCheck = 0;
450 for (size_t i = 0; i < saDeviceTypes.size(); ++ i)
451 if (saDeviceTypes[i] == devTypeRequested)
452 driveCheck++;
453 if (!driveCheck)
454 throw Utf8StrFmt("The given attachment is not supported by the storage controller '%s'", pszCtl);
455 }
456 else
457 goto leave;
458 }
459
460 // find the medium given
461 /* host drive? */
462 if (!RTStrNICmp(pszMedium, "host:", 5))
463 {
464 ComPtr<IHost> host;
465 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
466
467 if (devTypeRequested == DeviceType_DVD)
468 {
469 rc = host->FindHostDVDDrive(Bstr(pszMedium + 5).raw(),
470 pMedium2Mount.asOutParam());
471 if (!pMedium2Mount)
472 {
473 /* 2nd try: try with the real name, important on Linux+libhal */
474 char szPathReal[RTPATH_MAX];
475 if (RT_FAILURE(RTPathReal(pszMedium + 5, szPathReal, sizeof(szPathReal))))
476 throw Utf8StrFmt("Invalid host DVD drive name \"%s\"", pszMedium + 5);
477 rc = host->FindHostDVDDrive(Bstr(szPathReal).raw(),
478 pMedium2Mount.asOutParam());
479 if (!pMedium2Mount)
480 throw Utf8StrFmt("Invalid host DVD drive name \"%s\"", pszMedium + 5);
481 }
482 }
483 else
484 {
485 // floppy
486 rc = host->FindHostFloppyDrive(Bstr(pszMedium + 5).raw(),
487 pMedium2Mount.asOutParam());
488 if (!pMedium2Mount)
489 throw Utf8StrFmt("Invalid host floppy drive name \"%s\"", pszMedium + 5);
490 }
491 }
492 else if (!RTStrICmp(pszMedium, "iSCSI"))
493 {
494 /* check for required options */
495 if (bstrServer.isEmpty() || bstrTarget.isEmpty())
496 throw Utf8StrFmt("Parameters --server and --target are required for iSCSI media");
497
498 /** @todo move the location stuff to Main, which can use pfnComposeName
499 * from the disk backends to construct the location properly. Also do
500 * not use slashes to separate the parts, as otherwise only the last
501 * element containing information will be shown. */
502 Bstr bstrISCSIMedium;
503 if ( bstrLun.isEmpty()
504 || (bstrLun == "0")
505 || (bstrLun == "enc0")
506 )
507 bstrISCSIMedium = BstrFmt("%ls|%ls", bstrServer.raw(), bstrTarget.raw());
508 else
509 bstrISCSIMedium = BstrFmt("%ls|%ls|%ls", bstrServer.raw(), bstrTarget.raw(), bstrLun.raw());
510
511 CHECK_ERROR(a->virtualBox, CreateHardDisk(Bstr("iSCSI").raw(),
512 bstrISCSIMedium.raw(),
513 pMedium2Mount.asOutParam()));
514 if (FAILED(rc)) goto leave;
515 if (!bstrPort.isEmpty())
516 bstrServer = BstrFmt("%ls:%ls", bstrServer.raw(), bstrPort.raw());
517
518 // set the other iSCSI parameters as properties
519 com::SafeArray <BSTR> names;
520 com::SafeArray <BSTR> values;
521 Bstr("TargetAddress").detachTo(names.appendedRaw());
522 bstrServer.detachTo(values.appendedRaw());
523 Bstr("TargetName").detachTo(names.appendedRaw());
524 bstrTarget.detachTo(values.appendedRaw());
525
526 if (!bstrLun.isEmpty())
527 {
528 Bstr("LUN").detachTo(names.appendedRaw());
529 bstrLun.detachTo(values.appendedRaw());
530 }
531 if (!bstrUsername.isEmpty())
532 {
533 Bstr("InitiatorUsername").detachTo(names.appendedRaw());
534 bstrUsername.detachTo(values.appendedRaw());
535 }
536 if (!bstrPassword.isEmpty())
537 {
538 Bstr("InitiatorSecret").detachTo(names.appendedRaw());
539 bstrPassword.detachTo(values.appendedRaw());
540 }
541
542 /// @todo add --initiator option - until that happens rely on the
543 // defaults of the iSCSI initiator code. Setting it to a constant
544 // value does more harm than good, as the initiator name is supposed
545 // to identify a particular initiator uniquely.
546 // Bstr("InitiatorName").detachTo(names.appendedRaw());
547 // Bstr("iqn.2008-04.com.sun.virtualbox.initiator").detachTo(values.appendedRaw());
548
549 /// @todo add --targetName and --targetPassword options
550
551 if (fIntNet)
552 {
553 Bstr("HostIPStack").detachTo(names.appendedRaw());
554 Bstr("0").detachTo(values.appendedRaw());
555 }
556
557 CHECK_ERROR(pMedium2Mount, SetProperties(ComSafeArrayAsInParam(names),
558 ComSafeArrayAsInParam(values)));
559 if (FAILED(rc)) goto leave;
560 Bstr guid;
561 CHECK_ERROR(pMedium2Mount, COMGETTER(Id)(guid.asOutParam()));
562 if (FAILED(rc)) goto leave;
563 RTPrintf("iSCSI disk created. UUID: %s\n", Utf8Str(guid).c_str());
564 }
565 else
566 {
567 Bstr bstrMedium(pszMedium);
568 if (bstrMedium.isEmpty())
569 throw Utf8Str("Missing --medium argument");
570
571 rc = findOrOpenMedium(a, pszMedium, devTypeRequested,
572 pMedium2Mount, fSetNewUuid, NULL);
573 if (FAILED(rc) || !pMedium2Mount)
574 throw Utf8StrFmt("Invalid UUID or filename \"%s\"", pszMedium);
575 }
576
577 // set medium/parent medium UUID, if so desired
578 if (fSetNewUuid || fSetNewParentUuid)
579 {
580 CHECK_ERROR(pMedium2Mount, SetIDs(fSetNewUuid, bstrNewUuid.raw(),
581 fSetNewParentUuid, bstrNewParentUuid.raw()));
582 if (FAILED(rc))
583 throw Utf8Str("Failed to set the medium/parent medium UUID");
584 }
585
586 // set medium type, if so desired
587 if (pMedium2Mount && fSetMediumType)
588 {
589 CHECK_ERROR(pMedium2Mount, COMSETTER(Type)(mediumType));
590 if (FAILED(rc))
591 throw Utf8Str("Failed to set the medium type");
592 }
593
594 if (pMedium2Mount && !bstrComment.isEmpty())
595 {
596 CHECK_ERROR(pMedium2Mount, COMSETTER(Description)(bstrComment.raw()));
597 }
598
599 switch (devTypeRequested)
600 {
601 case DeviceType_DVD:
602 case DeviceType_Floppy:
603 {
604 if (!fRunTime)
605 {
606 ComPtr<IMediumAttachment> mediumAttachment;
607 // check if there is a dvd/floppy drive at the given location, if not attach one first
608 rc = machine->GetMediumAttachment(Bstr(pszCtl).raw(),
609 port,
610 device,
611 mediumAttachment.asOutParam());
612 if (SUCCEEDED(rc))
613 {
614 DeviceType_T deviceType;
615 mediumAttachment->COMGETTER(Type)(&deviceType);
616 if (deviceType != devTypeRequested)
617 {
618 machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
619 rc = machine->AttachDevice(Bstr(pszCtl).raw(),
620 port,
621 device,
622 devTypeRequested, // DeviceType_DVD or DeviceType_Floppy
623 NULL);
624 }
625 }
626 else
627 {
628 rc = machine->AttachDevice(Bstr(pszCtl).raw(),
629 port,
630 device,
631 devTypeRequested, // DeviceType_DVD or DeviceType_Floppy
632 NULL);
633 }
634 }
635
636 if (pMedium2Mount)
637 {
638 CHECK_ERROR(machine, MountMedium(Bstr(pszCtl).raw(),
639 port,
640 device,
641 pMedium2Mount,
642 fForceUnmount));
643 }
644 } // end DeviceType_DVD or DeviceType_Floppy:
645 break;
646
647 case DeviceType_HardDisk:
648 {
649 if (fRunTime)
650 throw Utf8Str("Hard disk attachments cannot be changed while the VM is running");
651
652 // if there is anything attached at the given location, remove it
653 machine->DetachDevice(Bstr(pszCtl).raw(), port, device);
654 CHECK_ERROR(machine, AttachDevice(Bstr(pszCtl).raw(),
655 port,
656 device,
657 DeviceType_HardDisk,
658 pMedium2Mount));
659 }
660 break;
661 }
662 }
663
664 if ( pszPassThrough
665 && (SUCCEEDED(rc)))
666 {
667 ComPtr<IMediumAttachment> mattach;
668 CHECK_ERROR(machine, GetMediumAttachment(Bstr(pszCtl).raw(), port,
669 device, mattach.asOutParam()));
670
671 if (SUCCEEDED(rc))
672 {
673 if (!RTStrICmp(pszPassThrough, "on"))
674 {
675 CHECK_ERROR(machine, PassthroughDevice(Bstr(pszCtl).raw(),
676 port, device, TRUE));
677 }
678 else if (!RTStrICmp(pszPassThrough, "off"))
679 {
680 CHECK_ERROR(machine, PassthroughDevice(Bstr(pszCtl).raw(),
681 port, device, FALSE));
682 }
683 else
684 throw Utf8StrFmt("Invalid --passthrough argument '%s'", pszPassThrough);
685 }
686 else
687 throw Utf8StrFmt("Couldn't find the controller attachment for the controller '%s'\n", pszCtl);
688 }
689
690 if ( pszBandwidthGroup
691 && !fRunTime
692 && SUCCEEDED(rc))
693 {
694
695 if (!RTStrICmp(pszBandwidthGroup, "none"))
696 {
697 /* Just remove the bandwidth gorup. */
698 CHECK_ERROR(machine, SetBandwidthGroupForDevice(Bstr(pszCtl).raw(),
699 port, device, NULL));
700 }
701 else
702 {
703 ComPtr<IBandwidthControl> bwCtrl;
704 ComPtr<IBandwidthGroup> bwGroup;
705
706 CHECK_ERROR(machine, COMGETTER(BandwidthControl)(bwCtrl.asOutParam()));
707
708 if (SUCCEEDED(rc))
709 {
710 CHECK_ERROR(bwCtrl, GetBandwidthGroup(Bstr(pszBandwidthGroup).raw(), bwGroup.asOutParam()));
711 if (SUCCEEDED(rc))
712 {
713 CHECK_ERROR(machine, SetBandwidthGroupForDevice(Bstr(pszCtl).raw(),
714 port, device, bwGroup));
715 }
716 }
717 }
718 }
719
720 /* commit changes */
721 if (SUCCEEDED(rc))
722 CHECK_ERROR(machine, SaveSettings());
723 }
724 catch (const Utf8Str &strError)
725 {
726 errorArgument("%s", strError.c_str());
727 rc = E_FAIL;
728 }
729
730 // machine must always be unlocked, even on errors
731leave:
732 a->session->UnlockMachine();
733
734 return SUCCEEDED(rc) ? 0 : 1;
735}
736
737
738static const RTGETOPTDEF g_aStorageControllerOptions[] =
739{
740 { "--name", 'n', RTGETOPT_REQ_STRING },
741 { "--add", 'a', RTGETOPT_REQ_STRING },
742 { "--controller", 'c', RTGETOPT_REQ_STRING },
743 { "--sataideemulation", 'e', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_INDEX },
744 { "--sataportcount", 'p', RTGETOPT_REQ_UINT32 },
745 { "--remove", 'r', RTGETOPT_REQ_NOTHING },
746 { "--hostiocache", 'i', RTGETOPT_REQ_STRING },
747 { "--bootable", 'b', RTGETOPT_REQ_STRING },
748};
749
750int handleStorageController(HandlerArg *a)
751{
752 int c;
753 HRESULT rc = S_OK;
754 const char *pszCtl = NULL;
755 const char *pszBusType = NULL;
756 const char *pszCtlType = NULL;
757 const char *pszHostIOCache = NULL;
758 const char *pszBootable = NULL;
759 ULONG satabootdev = ~0U;
760 ULONG sataidedev = ~0U;
761 ULONG sataportcount = ~0U;
762 bool fRemoveCtl = false;
763 ComPtr<IMachine> machine;
764 RTGETOPTUNION ValueUnion;
765 RTGETOPTSTATE GetState;
766
767 if (a->argc < 4)
768 return errorSyntax(USAGE_STORAGECONTROLLER, "Too few parameters");
769
770 RTGetOptInit (&GetState, a->argc, a->argv, g_aStorageControllerOptions,
771 RT_ELEMENTS(g_aStorageControllerOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
772
773 while ( SUCCEEDED(rc)
774 && (c = RTGetOpt(&GetState, &ValueUnion)))
775 {
776 switch (c)
777 {
778 case 'n': // controller name
779 {
780 if (ValueUnion.psz)
781 pszCtl = ValueUnion.psz;
782 else
783 rc = E_FAIL;
784 break;
785 }
786
787 case 'a': // controller bus type <ide/sata/scsi/floppy>
788 {
789 if (ValueUnion.psz)
790 pszBusType = ValueUnion.psz;
791 else
792 rc = E_FAIL;
793 break;
794 }
795
796 case 'c': // controller <lsilogic/buslogic/intelahci/piix3/piix4/ich6/i82078>
797 {
798 if (ValueUnion.psz)
799 pszCtlType = ValueUnion.psz;
800 else
801 rc = E_FAIL;
802 break;
803 }
804
805 case 'e': // sataideemulation
806 {
807 satabootdev = GetState.uIndex;
808 sataidedev = ValueUnion.u32;
809 break;
810 }
811
812 case 'p': // sataportcount
813 {
814 sataportcount = ValueUnion.u32;
815 break;
816 }
817
818 case 'r': // remove controller
819 {
820 fRemoveCtl = true;
821 break;
822 }
823
824 case 'i':
825 {
826 pszHostIOCache = ValueUnion.psz;
827 break;
828 }
829
830 case 'b':
831 {
832 pszBootable = ValueUnion.psz;
833 break;
834 }
835
836 default:
837 {
838 errorGetOpt(USAGE_STORAGECONTROLLER, c, &ValueUnion);
839 rc = E_FAIL;
840 break;
841 }
842 }
843 }
844
845 if (FAILED(rc))
846 return 1;
847
848 /* try to find the given machine */
849 CHECK_ERROR_RET(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
850 machine.asOutParam()), 1);
851
852 /* open a session for the VM */
853 CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Write), 1);
854
855 /* get the mutable session machine */
856 a->session->COMGETTER(Machine)(machine.asOutParam());
857
858 if (!pszCtl)
859 {
860 /* it's important to always close sessions */
861 a->session->UnlockMachine();
862 errorSyntax(USAGE_STORAGECONTROLLER, "Storage controller name not specified\n");
863 return 1;
864 }
865
866 if (fRemoveCtl)
867 {
868 com::SafeIfaceArray<IMediumAttachment> mediumAttachments;
869
870 CHECK_ERROR(machine,
871 GetMediumAttachmentsOfController(Bstr(pszCtl).raw(),
872 ComSafeArrayAsOutParam(mediumAttachments)));
873 for (size_t i = 0; i < mediumAttachments.size(); ++ i)
874 {
875 ComPtr<IMediumAttachment> mediumAttach = mediumAttachments[i];
876 LONG port = 0;
877 LONG device = 0;
878
879 CHECK_ERROR(mediumAttach, COMGETTER(Port)(&port));
880 CHECK_ERROR(mediumAttach, COMGETTER(Device)(&device));
881 CHECK_ERROR(machine, DetachDevice(Bstr(pszCtl).raw(), port, device));
882 }
883
884 if (SUCCEEDED(rc))
885 CHECK_ERROR(machine, RemoveStorageController(Bstr(pszCtl).raw()));
886 else
887 errorArgument("Can't detach the devices connected to '%s' Controller\n"
888 "and thus its removal failed.", pszCtl);
889 }
890 else
891 {
892 if (pszBusType)
893 {
894 ComPtr<IStorageController> ctl;
895
896 if (!RTStrICmp(pszBusType, "ide"))
897 {
898 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
899 StorageBus_IDE,
900 ctl.asOutParam()));
901 }
902 else if (!RTStrICmp(pszBusType, "sata"))
903 {
904 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
905 StorageBus_SATA,
906 ctl.asOutParam()));
907 }
908 else if (!RTStrICmp(pszBusType, "scsi"))
909 {
910 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
911 StorageBus_SCSI,
912 ctl.asOutParam()));
913 }
914 else if (!RTStrICmp(pszBusType, "floppy"))
915 {
916 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
917 StorageBus_Floppy,
918 ctl.asOutParam()));
919 }
920 else if (!RTStrICmp(pszBusType, "sas"))
921 {
922 CHECK_ERROR(machine, AddStorageController(Bstr(pszCtl).raw(),
923 StorageBus_SAS,
924 ctl.asOutParam()));
925 }
926 else
927 {
928 errorArgument("Invalid --add argument '%s'", pszBusType);
929 rc = E_FAIL;
930 }
931 }
932
933 if ( pszCtlType
934 && SUCCEEDED(rc))
935 {
936 ComPtr<IStorageController> ctl;
937
938 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
939 ctl.asOutParam()));
940
941 if (SUCCEEDED(rc))
942 {
943 if (!RTStrICmp(pszCtlType, "lsilogic"))
944 {
945 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogic));
946 }
947 else if (!RTStrICmp(pszCtlType, "buslogic"))
948 {
949 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_BusLogic));
950 }
951 else if (!RTStrICmp(pszCtlType, "intelahci"))
952 {
953 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_IntelAhci));
954 }
955 else if (!RTStrICmp(pszCtlType, "piix3"))
956 {
957 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_PIIX3));
958 }
959 else if (!RTStrICmp(pszCtlType, "piix4"))
960 {
961 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_PIIX4));
962 }
963 else if (!RTStrICmp(pszCtlType, "ich6"))
964 {
965 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_ICH6));
966 }
967 else if (!RTStrICmp(pszCtlType, "i82078"))
968 {
969 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_I82078));
970 }
971 else if (!RTStrICmp(pszCtlType, "lsilogicsas"))
972 {
973 CHECK_ERROR(ctl, COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas));
974 }
975 else
976 {
977 errorArgument("Invalid --type argument '%s'", pszCtlType);
978 rc = E_FAIL;
979 }
980 }
981 else
982 {
983 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
984 rc = E_FAIL;
985 }
986 }
987
988 if ( (sataportcount != ~0U)
989 && SUCCEEDED(rc))
990 {
991 ComPtr<IStorageController> ctl;
992
993 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
994 ctl.asOutParam()));
995
996 if (SUCCEEDED(rc))
997 {
998 CHECK_ERROR(ctl, COMSETTER(PortCount)(sataportcount));
999 }
1000 else
1001 {
1002 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
1003 rc = E_FAIL;
1004 }
1005 }
1006
1007 if ( (sataidedev != ~0U)
1008 && (satabootdev != ~0U)
1009 && SUCCEEDED(rc))
1010 {
1011 ComPtr<IStorageController> ctl;
1012
1013 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1014 ctl.asOutParam()));
1015
1016 if (SUCCEEDED(rc))
1017 {
1018 CHECK_ERROR(ctl, SetIDEEmulationPort(satabootdev, sataidedev));
1019 }
1020 else
1021 {
1022 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
1023 rc = E_FAIL;
1024 }
1025 }
1026
1027 if ( pszHostIOCache
1028 && SUCCEEDED(rc))
1029 {
1030 ComPtr<IStorageController> ctl;
1031
1032 CHECK_ERROR(machine, GetStorageControllerByName(Bstr(pszCtl).raw(),
1033 ctl.asOutParam()));
1034
1035 if (SUCCEEDED(rc))
1036 {
1037 if (!RTStrICmp(pszHostIOCache, "on"))
1038 {
1039 CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(TRUE));
1040 }
1041 else if (!RTStrICmp(pszHostIOCache, "off"))
1042 {
1043 CHECK_ERROR(ctl, COMSETTER(UseHostIOCache)(FALSE));
1044 }
1045 else
1046 {
1047 errorArgument("Invalid --hostiocache argument '%s'", pszHostIOCache);
1048 rc = E_FAIL;
1049 }
1050 }
1051 else
1052 {
1053 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
1054 rc = E_FAIL;
1055 }
1056 }
1057
1058 if ( pszBootable
1059 && SUCCEEDED(rc))
1060 {
1061 if (SUCCEEDED(rc))
1062 {
1063 if (!RTStrICmp(pszBootable, "on"))
1064 {
1065 CHECK_ERROR(machine, SetStorageControllerBootable(Bstr(pszCtl).raw(), TRUE));
1066 }
1067 else if (!RTStrICmp(pszBootable, "off"))
1068 {
1069 CHECK_ERROR(machine, SetStorageControllerBootable(Bstr(pszCtl).raw(), FALSE));
1070 }
1071 else
1072 {
1073 errorArgument("Invalid --bootable argument '%s'", pszBootable);
1074 rc = E_FAIL;
1075 }
1076 }
1077 else
1078 {
1079 errorArgument("Couldn't find the controller with the name: '%s'\n", pszCtl);
1080 rc = E_FAIL;
1081 }
1082 }
1083 }
1084
1085 /* commit changes */
1086 if (SUCCEEDED(rc))
1087 CHECK_ERROR(machine, SaveSettings());
1088
1089 /* it's important to always close sessions */
1090 a->session->UnlockMachine();
1091
1092 return SUCCEEDED(rc) ? 0 : 1;
1093}
1094
1095#endif /* !VBOX_ONLY_DOCS */
1096
Note: See TracBrowser for help on using the repository browser.

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