VirtualBox

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

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

Frontends/VBoxManage: add a way to specify the iSCSI initiator (should only be used by people who know what they're doing, the default is normally the best choice)

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