VirtualBox

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

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

Main/Machine+MediumAttachment+Console: add method for marking a 'hard disk' as non-rotational, which optimizes performance in modern guest OSes
Frontends/VBoxManage+VirtualBox: support the new method
ChangeLog: add quite a few forgotten improvements and fixes

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

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