VirtualBox

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

Last change on this file since 49725 was 49190, checked in by vboxsync, 11 years ago

Main,VBoxManage: Implement hotpluggable setting stubs

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