VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageDHCPServer.cpp@ 78212

Last change on this file since 78212 was 77595, checked in by vboxsync, 6 years ago

VBoxManage: Re-do help category handling (command/sub-command specific help) for old-style commands to lift the low limit of the bit field approach for everything. This is now much closer to the handling of new-style commands, but far from converting anything old. While at it I eliminated the need to define an entry in the (old-style) USAGECATEGORY enum for new-style commands.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.1 KB
Line 
1/* $Id: VBoxManageDHCPServer.cpp 77595 2019-03-07 12:42:31Z vboxsync $ */
2/** @file
3 * VBoxManage - Implementation of dhcpserver command.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#ifndef VBOX_ONLY_DOCS
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#endif /* !VBOX_ONLY_DOCS */
29
30#include <iprt/cidr.h>
31#include <iprt/param.h>
32#include <iprt/path.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35#include <iprt/net.h>
36#include <iprt/getopt.h>
37#include <iprt/ctype.h>
38
39#include <VBox/log.h>
40
41#include "VBoxManage.h"
42
43#include <string>
44#include <vector>
45#include <map>
46
47#ifndef VBOX_ONLY_DOCS
48using namespace com;
49
50typedef enum enMainOpCodes
51{
52 OP_ADD = 1000,
53 OP_REMOVE,
54 OP_MODIFY,
55 OP_RESTART
56} OPCODE;
57
58typedef std::pair<DhcpOpt_T, std::string> DhcpOptSpec;
59typedef std::vector<DhcpOptSpec> DhcpOpts;
60typedef DhcpOpts::iterator DhcpOptIterator;
61
62typedef std::vector<DhcpOpt_T> DhcpOptIds;
63typedef DhcpOptIds::iterator DhcpOptIdIterator;
64
65struct VmNameSlotKey
66{
67 const std::string VmName;
68 uint8_t u8Slot;
69
70 VmNameSlotKey(const std::string &aVmName, uint8_t aSlot)
71 : VmName(aVmName)
72 , u8Slot(aSlot)
73 {}
74
75 bool operator< (const VmNameSlotKey& that) const
76 {
77 if (VmName == that.VmName)
78 return u8Slot < that.u8Slot;
79 else
80 return VmName < that.VmName;
81 }
82};
83
84typedef std::map<VmNameSlotKey, DhcpOpts> VmSlot2OptionsM;
85typedef VmSlot2OptionsM::iterator VmSlot2OptionsIterator;
86typedef VmSlot2OptionsM::value_type VmSlot2OptionsPair;
87
88typedef std::map<VmNameSlotKey, DhcpOptIds> VmSlot2OptionIdsM;
89typedef VmSlot2OptionIdsM::iterator VmSlot2OptionIdsIterator;
90
91typedef std::vector<VmNameSlotKey> VmConfigs;
92
93static const RTGETOPTDEF g_aDHCPIPOptions[] =
94{
95 { "--netname", 't', RTGETOPT_REQ_STRING }, /* we use 't' instead of 'n' to avoid
96 * 1. the misspelled "-enable" long option to be treated as 'e' (for -enable) + 'n' (for -netname) + "<the_rest_opt>" (for net name)
97 * 2. the misspelled "-netmask" to be treated as 'n' (for -netname) + "<the_rest_opt>" (for net name)
98 */
99 { "-netname", 't', RTGETOPT_REQ_STRING }, // deprecated (if removed check below)
100 { "--ifname", 'f', RTGETOPT_REQ_STRING }, /* we use 'f' instead of 'i' to avoid
101 * 1. the misspelled "-disable" long option to be treated as 'd' (for -disable) + 'i' (for -ifname) + "<the_rest_opt>" (for if name)
102 */
103 { "-ifname", 'f', RTGETOPT_REQ_STRING }, // deprecated
104 { "--ip", 'a', RTGETOPT_REQ_STRING },
105 { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
106 { "--netmask", 'm', RTGETOPT_REQ_STRING },
107 { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated
108 { "--lowerip", 'l', RTGETOPT_REQ_STRING },
109 { "-lowerip", 'l', RTGETOPT_REQ_STRING }, // deprecated
110 { "--upperip", 'u', RTGETOPT_REQ_STRING },
111 { "-upperip", 'u', RTGETOPT_REQ_STRING }, // deprecated
112 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
113 { "-enable", 'e', RTGETOPT_REQ_NOTHING }, // deprecated
114 { "--disable", 'd', RTGETOPT_REQ_NOTHING },
115 { "-disable", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
116 { "--options", 'o', RTGETOPT_REQ_NOTHING },
117 { "--vm", 'M', RTGETOPT_REQ_STRING}, /* only with -o */
118 { "--nic", 'n', RTGETOPT_REQ_UINT8}, /* only with -o and -M */
119 { "--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */
120 { "--value", 'p', RTGETOPT_REQ_STRING}, /* only with -i */
121 { "--remove", 'r', RTGETOPT_REQ_NOTHING} /* only with -i */
122};
123
124static RTEXITCODE handleOp(HandlerArg *a, OPCODE enmCode, int iStart)
125{
126 if (a->argc - iStart < 2)
127 return errorSyntax(USAGE_DHCPSERVER, "Not enough parameters");
128
129 int index = iStart;
130 HRESULT rc;
131 bool fOptionsRead = false;
132 bool fVmOptionRead = false;
133
134 const char *pszVmName = NULL;
135 const char *pNetName = NULL;
136 const char *pIfName = NULL;
137 const char * pIp = NULL;
138 const char * pNetmask = NULL;
139 const char * pLowerIp = NULL;
140 const char * pUpperIp = NULL;
141
142 uint8_t u8OptId = (uint8_t)~0;
143 uint8_t u8Slot = (uint8_t)~0;
144
145 int enable = -1;
146
147 DhcpOpts GlobalDhcpOptions;
148 DhcpOptIds GlobalDhcpOptions2Delete;
149 VmSlot2OptionsM VmSlot2Options;
150 VmSlot2OptionIdsM VmSlot2Options2Delete;
151 VmConfigs VmConfigs2Delete;
152
153 int c;
154 RTGETOPTUNION ValueUnion;
155 RTGETOPTSTATE GetState;
156 RTGetOptInit(&GetState,
157 a->argc,
158 a->argv,
159 g_aDHCPIPOptions,
160 enmCode != OP_REMOVE ? RT_ELEMENTS(g_aDHCPIPOptions) : 4, /* we use only --netname and --ifname for remove*/
161 index,
162 RTGETOPTINIT_FLAGS_NO_STD_OPTS);
163 while ((c = RTGetOpt(&GetState, &ValueUnion)))
164 {
165 switch (c)
166 {
167 case 't': // --netname
168 if(pNetName)
169 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --netname once.");
170 else if (pIfName)
171 return errorSyntax(USAGE_DHCPSERVER, "You can either use a --netname or --ifname for identifying the DHCP server.");
172 else
173 {
174 pNetName = ValueUnion.psz;
175 }
176 break;
177 case 'f': // --ifname
178 if(pIfName)
179 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --ifname once.");
180 else if (pNetName)
181 return errorSyntax(USAGE_DHCPSERVER, "You can either use a --netname or --ipname for identifying the DHCP server.");
182 else
183 {
184 pIfName = ValueUnion.psz;
185 }
186 break;
187 case 'a': // -ip
188 if(pIp)
189 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --ip once.");
190 else
191 {
192 pIp = ValueUnion.psz;
193 }
194 break;
195 case 'm': // --netmask
196 if(pNetmask)
197 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --netmask once.");
198 else
199 {
200 pNetmask = ValueUnion.psz;
201 }
202 break;
203 case 'l': // --lowerip
204 if(pLowerIp)
205 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --lowerip once.");
206 else
207 {
208 pLowerIp = ValueUnion.psz;
209 }
210 break;
211 case 'u': // --upperip
212 if(pUpperIp)
213 return errorSyntax(USAGE_DHCPSERVER, "You can only specify --upperip once.");
214 else
215 {
216 pUpperIp = ValueUnion.psz;
217 }
218 break;
219 case 'e': // --enable
220 if(enable >= 0)
221 return errorSyntax(USAGE_DHCPSERVER, "You can specify either --enable or --disable once.");
222 else
223 {
224 enable = 1;
225 }
226 break;
227 case 'd': // --disable
228 if(enable >= 0)
229 return errorSyntax(USAGE_DHCPSERVER, "You can specify either --enable or --disable once.");
230 else
231 {
232 enable = 0;
233 }
234 break;
235 case VINF_GETOPT_NOT_OPTION:
236 return errorSyntax(USAGE_DHCPSERVER, "unhandled parameter: %s", ValueUnion.psz);
237 break;
238
239 case 'o': // --options
240 {
241 // {"--vm", 'n', RTGETOPT_REQ_STRING}, /* only with -o */
242 // {"--nic", 'c', RTGETOPT_REQ_UINT8}, /* only with -o and -n*/
243 // {"--id", 'i', RTGETOPT_REQ_UINT8}, /* only with -o */
244 // {"--value", 'p', RTGETOPT_REQ_STRING} /* only with -i */
245 if (fOptionsRead)
246 return errorSyntax(USAGE_DHCPSERVER,
247 "previos option edition wasn't finished");
248 fOptionsRead = true;
249 fVmOptionRead = false; /* we want specify new global or vm option*/
250 u8Slot = (uint8_t)~0;
251 u8OptId = (uint8_t)~0;
252 pszVmName = NULL;
253 } /* end of --options */
254 break;
255
256 case 'M': // --vm
257 {
258 if (fVmOptionRead)
259 return errorSyntax(USAGE_DHCPSERVER,
260 "previous vm option edition wasn't finished");
261 else
262 fVmOptionRead = true;
263 u8Slot = (uint8_t)~0; /* clear slot */
264 pszVmName = RTStrDup(ValueUnion.psz);
265 }
266 break; /* end of --vm */
267
268 case 'n': // --nic
269 {
270 if (!fVmOptionRead)
271 return errorSyntax(USAGE_DHCPSERVER,
272 "vm name wasn't specified");
273
274 u8Slot = ValueUnion.u8;
275
276 if (u8Slot < 1)
277 return errorSyntax(USAGE_DHCPSERVER,
278 "invalid NIC number: %u", u8Slot);
279 --u8Slot;
280 }
281 break; /* end of --nic */
282
283 case 'i': // --id
284 {
285 if (!fOptionsRead)
286 return errorSyntax(USAGE_DHCPSERVER,
287 "-o wasn't found");
288
289 u8OptId = ValueUnion.u8;
290 }
291 break; /* end of --id */
292
293 case 'p': // --value
294 {
295 if (!fOptionsRead)
296 return errorSyntax(USAGE_DHCPSERVER,
297 "-o wasn't found");
298
299 if (u8OptId == (uint8_t)~0)
300 return errorSyntax(USAGE_DHCPSERVER,
301 "--id wasn't found");
302 if ( fVmOptionRead
303 && u8Slot == (uint8_t)~0)
304 return errorSyntax(USAGE_DHCPSERVER,
305 "--nic wasn't found");
306
307 DhcpOpts &opts = fVmOptionRead ? VmSlot2Options[VmNameSlotKey(pszVmName, u8Slot)]
308 : GlobalDhcpOptions;
309 std::string strVal = ValueUnion.psz;
310 opts.push_back(DhcpOptSpec((DhcpOpt_T)u8OptId, strVal));
311
312 }
313 break; // --end of value
314
315 case 'r': /* --remove */
316 {
317 if (!fOptionsRead)
318 return errorSyntax(USAGE_DHCPSERVER,
319 "-o wasn't found");
320
321 if (u8OptId == (uint8_t)~0)
322 return errorSyntax(USAGE_DHCPSERVER,
323 "--id wasn't found");
324 if ( fVmOptionRead
325 && u8Slot == (uint8_t)~0)
326 return errorSyntax(USAGE_DHCPSERVER,
327 "--nic wasn't found");
328
329 DhcpOptIds &optIds = fVmOptionRead ? VmSlot2Options2Delete[VmNameSlotKey(pszVmName, u8Slot)]
330 : GlobalDhcpOptions2Delete;
331 optIds.push_back((DhcpOpt_T)u8OptId);
332 }
333 break; /* --end of remove */
334
335 default:
336 if (c > 0)
337 {
338 if (RT_C_IS_GRAPH(c))
339 return errorSyntax(USAGE_DHCPSERVER, "unhandled option: -%c", c);
340 return errorSyntax(USAGE_DHCPSERVER, "unhandled option: %i", c);
341 }
342 if (c == VERR_GETOPT_UNKNOWN_OPTION)
343 return errorSyntax(USAGE_DHCPSERVER, "unknown option: %s", ValueUnion.psz);
344 if (ValueUnion.pDef)
345 return errorSyntax(USAGE_DHCPSERVER, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
346 return errorSyntax(USAGE_DHCPSERVER, "%Rrs", c);
347 }
348 }
349
350 if(! pNetName && !pIfName)
351 return errorSyntax(USAGE_DHCPSERVER, "You need to specify either --netname or --ifname to identify the DHCP server");
352
353 if( enmCode != OP_REMOVE
354 && enmCode != OP_RESTART
355 && GlobalDhcpOptions.empty()
356 && VmSlot2Options.empty()
357 && GlobalDhcpOptions2Delete.empty()
358 && VmSlot2Options2Delete.empty())
359 {
360 if(enable < 0 || pIp || pNetmask || pLowerIp || pUpperIp)
361 {
362 if(!pIp)
363 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --ip option");
364
365 if(!pNetmask)
366 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --netmask option");
367
368 if(!pLowerIp)
369 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --lowerip option");
370
371 if(!pUpperIp)
372 return errorSyntax(USAGE_DHCPSERVER, "You need to specify --upperip option");
373 }
374 }
375
376 Bstr NetName;
377 if(!pNetName)
378 {
379 ComPtr<IHost> host;
380 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
381
382 ComPtr<IHostNetworkInterface> hif;
383 CHECK_ERROR(host, FindHostNetworkInterfaceByName(Bstr(pIfName).mutableRaw(), hif.asOutParam()));
384 if (FAILED(rc))
385 return errorArgument("Could not find interface '%s'", pIfName);
386
387 CHECK_ERROR(hif, COMGETTER(NetworkName) (NetName.asOutParam()));
388 if (FAILED(rc))
389 return errorArgument("Could not get network name for the interface '%s'", pIfName);
390 }
391 else
392 {
393 NetName = Bstr(pNetName);
394 }
395
396 ComPtr<IDHCPServer> svr;
397 rc = a->virtualBox->FindDHCPServerByNetworkName(NetName.mutableRaw(), svr.asOutParam());
398 if(enmCode == OP_ADD)
399 {
400 if (SUCCEEDED(rc))
401 return errorArgument("DHCP server already exists");
402
403 CHECK_ERROR(a->virtualBox, CreateDHCPServer(NetName.mutableRaw(), svr.asOutParam()));
404 if (FAILED(rc))
405 return errorArgument("Failed to create the DHCP server");
406 }
407 else if (FAILED(rc))
408 {
409 return errorArgument("DHCP server does not exist");
410 }
411
412 if (enmCode == OP_RESTART)
413 {
414 CHECK_ERROR(svr, Restart());
415 if(FAILED(rc))
416 return errorArgument("Failed to restart server");
417 }
418 else if (enmCode == OP_REMOVE)
419 {
420 CHECK_ERROR(a->virtualBox, RemoveDHCPServer(svr));
421 if(FAILED(rc))
422 return errorArgument("Failed to remove server");
423 }
424 else
425 {
426 if (pIp || pNetmask || pLowerIp || pUpperIp)
427 {
428 CHECK_ERROR(svr, SetConfiguration (
429 Bstr(pIp).mutableRaw(),
430 Bstr(pNetmask).mutableRaw(),
431 Bstr(pLowerIp).mutableRaw(),
432 Bstr(pUpperIp).mutableRaw()));
433 if(FAILED(rc))
434 return errorArgument("Failed to set configuration");
435 }
436
437 if(enable >= 0)
438 {
439 CHECK_ERROR(svr, COMSETTER(Enabled) ((BOOL)enable));
440 }
441
442 /* remove specified options */
443 DhcpOptIdIterator itOptId;
444 for (itOptId = GlobalDhcpOptions2Delete.begin();
445 itOptId != GlobalDhcpOptions2Delete.end();
446 ++itOptId)
447 {
448 CHECK_ERROR(svr, RemoveGlobalOption(*itOptId));
449 }
450 VmSlot2OptionIdsIterator itIdVector;
451 for (itIdVector = VmSlot2Options2Delete.begin();
452 itIdVector != VmSlot2Options2Delete.end();
453 ++itIdVector)
454 {
455 for(itOptId = itIdVector->second.begin();
456 itOptId != itIdVector->second.end();
457 ++itOptId)
458 {
459 CHECK_ERROR(svr,
460 RemoveVmSlotOption(Bstr(itIdVector->first.VmName.c_str()).raw(),
461 itIdVector->first.u8Slot,
462 *itOptId));
463 }
464 }
465
466 /* option processing */
467 DhcpOptIterator itOpt;
468 VmSlot2OptionsIterator it;
469
470 /* Global Options */
471 for(itOpt = GlobalDhcpOptions.begin();
472 itOpt != GlobalDhcpOptions.end();
473 ++itOpt)
474 {
475 CHECK_ERROR(svr,
476 AddGlobalOption(
477 itOpt->first,
478 com::Bstr(itOpt->second.c_str()).raw()));
479 }
480
481 /* heh, vm slot options. */
482
483 for (it = VmSlot2Options.begin();
484 it != VmSlot2Options.end();
485 ++it)
486 {
487 for(itOpt = it->second.begin();
488 itOpt != it->second.end();
489 ++itOpt)
490 {
491 CHECK_ERROR(svr,
492 AddVmSlotOption(Bstr(it->first.VmName.c_str()).raw(),
493 it->first.u8Slot,
494 itOpt->first,
495 com::Bstr(itOpt->second.c_str()).raw()));
496 }
497 }
498 }
499
500 return RTEXITCODE_SUCCESS;
501}
502
503
504RTEXITCODE handleDHCPServer(HandlerArg *a)
505{
506 if (a->argc < 1)
507 return errorSyntax(USAGE_DHCPSERVER, "Not enough parameters");
508
509 RTEXITCODE rcExit;
510 if (strcmp(a->argv[0], "modify") == 0)
511 rcExit = handleOp(a, OP_MODIFY, 1);
512 else if (strcmp(a->argv[0], "add") == 0)
513 rcExit = handleOp(a, OP_ADD, 1);
514 else if (strcmp(a->argv[0], "remove") == 0)
515 rcExit = handleOp(a, OP_REMOVE, 1);
516 else if (strcmp(a->argv[0], "restart") == 0)
517 rcExit = handleOp(a, OP_RESTART, 1);
518 else
519 rcExit = errorSyntax(USAGE_DHCPSERVER, "Invalid parameter '%s'", Utf8Str(a->argv[0]).c_str());
520
521 return rcExit;
522}
523
524#endif /* !VBOX_ONLY_DOCS */
525
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