VirtualBox

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

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

VBoxManage storageattach: make the --device parameter optional

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