VirtualBox

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

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

Main/MediumAttachment+Machine: add a setting which controls the guest-triggered medium eject behavior, fix handling "implicit" media, and corresponding VBoxManage and documentation updates

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