VirtualBox

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

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

Frontends/VBoxManage: make --port and --device parameter for storageattach optional if there is only one port or device per port

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