VirtualBox

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

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

Frontends/VBoxManage: more paranoid error handling for finding media in storageattach

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