VirtualBox

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

Last change on this file since 79611 was 79611, checked in by vboxsync, 5 years ago

VBoxManage,manual: Added the dhcpserver subcommand findlease for testing IDHCPServer::FindLeaseByMAC(). Wrote a man page for the dhcpserver command and converted the help to the new style. Also took a shot at simplifying the option handling of the existing dhcpserver subcommands while retaining a somewhat more flexible compatibility with the old syntax (not advertised) - not really tested. bugref:9288 bugref:9151

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.3 KB
Line 
1/* $Id: VBoxManageDHCPServer.cpp 79611 2019-07-08 23:33:59Z 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
47using namespace com;
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53#define DHCPD_CMD_COMMON_OPT_NETWORK 999 /**< The --network / --netname option number. */
54#define DHCPD_CMD_COMMON_OPT_INTERFACE 998 /**< The --interface / --ifname option number. */
55/** Common option definitions. */
56#define DHCPD_CMD_COMMON_OPTION_DEFS() \
57 { "--network", DHCPD_CMD_COMMON_OPT_NETWORK, RTGETOPT_REQ_STRING }, \
58 { "--netname", DHCPD_CMD_COMMON_OPT_NETWORK, RTGETOPT_REQ_STRING }, /* legacy */ \
59 { "--interface", DHCPD_CMD_COMMON_OPT_INTERFACE, RTGETOPT_REQ_STRING }, \
60 { "--ifname", DHCPD_CMD_COMMON_OPT_INTERFACE, RTGETOPT_REQ_STRING } /* legacy
61
62/** Handles common options in the typical option parsing switch. */
63#define DHCPD_CMD_COMMON_OPTION_CASES(a_pCtx, a_ch, a_pValueUnion) \
64 case DHCPD_CMD_COMMON_OPT_NETWORK: \
65 if ((a_pCtx)->pszInterface != NULL) \
66 return errorSyntax("Either --network or --interface, not both"); \
67 (a_pCtx)->pszNetwork = ValueUnion.psz; \
68 break; \
69 case DHCPD_CMD_COMMON_OPT_INTERFACE: \
70 if ((a_pCtx)->pszNetwork != NULL) \
71 return errorSyntax("Either --interface or --network, not both"); \
72 (a_pCtx)->pszInterface = ValueUnion.psz; \
73 break
74
75
76/*********************************************************************************************************************************
77* Structures and Typedefs *
78*********************************************************************************************************************************/
79/**
80 * Definition of a dhcpserver command, with handler and various flags.
81 */
82typedef struct DHCPDCMDDEF
83{
84 /** The command name. */
85 const char *pszName;
86
87 /**
88 * Actual command handler callback.
89 *
90 * @param pCtx Pointer to command context to use.
91 */
92 DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (struct DHCPDCMDCTX *pCtx, int argc, char **argv));
93
94 /** The sub-command scope flags. */
95 uint64_t fSubcommandScope;
96} DHCPDCMDDEF;
97/** Pointer to a const dhcpserver command definition. */
98typedef DHCPDCMDDEF const *PCDHCPDCMDDEF;
99
100/**
101 * dhcpserver command context (mainly for carrying common options and such).
102 */
103typedef struct DHCPDCMDCTX
104{
105 /** The handler arguments from the main() function. */
106 HandlerArg *pArg;
107 /** Pointer to the command definition. */
108 PCDHCPDCMDDEF pCmdDef;
109 /** The network name. */
110 const char *pszNetwork;
111 /** The (trunk) interface name. */
112 const char *pszInterface;
113} DHCPDCMDCTX;
114/** Pointer to a dhcpserver command context. */
115typedef DHCPDCMDCTX *PDHCPDCMDCTX;
116
117
118
119typedef enum enMainOpCodes
120{
121 OP_ADD = 1000,
122 OP_REMOVE,
123 OP_MODIFY,
124 OP_RESTART
125} OPCODE;
126
127typedef std::pair<DhcpOpt_T, std::string> DhcpOptSpec;
128typedef std::vector<DhcpOptSpec> DhcpOpts;
129typedef DhcpOpts::iterator DhcpOptIterator;
130
131typedef std::vector<DhcpOpt_T> DhcpOptIds;
132typedef DhcpOptIds::iterator DhcpOptIdIterator;
133
134struct VmNameSlotKey
135{
136 const std::string VmName;
137 uint8_t u8Slot;
138
139 VmNameSlotKey(const std::string &aVmName, uint8_t aSlot)
140 : VmName(aVmName)
141 , u8Slot(aSlot)
142 {}
143
144 bool operator< (const VmNameSlotKey& that) const
145 {
146 if (VmName == that.VmName)
147 return u8Slot < that.u8Slot;
148 else
149 return VmName < that.VmName;
150 }
151};
152
153typedef std::map<VmNameSlotKey, DhcpOpts> VmSlot2OptionsM;
154typedef VmSlot2OptionsM::iterator VmSlot2OptionsIterator;
155typedef VmSlot2OptionsM::value_type VmSlot2OptionsPair;
156
157typedef std::map<VmNameSlotKey, DhcpOptIds> VmSlot2OptionIdsM;
158typedef VmSlot2OptionIdsM::iterator VmSlot2OptionIdsIterator;
159
160typedef std::vector<VmNameSlotKey> VmConfigs;
161
162
163/*********************************************************************************************************************************
164* Global Variables *
165*********************************************************************************************************************************/
166
167
168
169static RTEXITCODE dhcpdHandleTooManyCmds(PDHCPDCMDCTX pCtx, int argc, char **argv)
170{
171 // glue - start
172 HandlerArg *a = pCtx->pArg;
173 OPCODE enmCode = pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_ADD ? OP_ADD
174 : pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_MODIFY ? OP_MODIFY
175 : pCtx->pCmdDef->fSubcommandScope == HELP_SCOPE_DHCPSERVER_REMOVE ? OP_REMOVE : OP_RESTART;
176 RT_NOREF(argc, argv);
177 // glue - end
178
179 static const RTGETOPTDEF s_aDHCPIPOptions[] =
180 {
181 DHCPD_CMD_COMMON_OPTION_DEFS(),
182 { "--ip", 'a', RTGETOPT_REQ_STRING },
183 { "-ip", 'a', RTGETOPT_REQ_STRING }, // deprecated
184 { "--netmask", 'm', RTGETOPT_REQ_STRING },
185 { "-netmask", 'm', RTGETOPT_REQ_STRING }, // deprecated
186 { "--lowerip", 'l', RTGETOPT_REQ_STRING },
187 { "-lowerip", 'l', RTGETOPT_REQ_STRING }, // deprecated
188 { "--upperip", 'u', RTGETOPT_REQ_STRING },
189 { "-upperip", 'u', RTGETOPT_REQ_STRING }, // deprecated
190 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
191 { "-enable", 'e', RTGETOPT_REQ_NOTHING }, // deprecated
192 { "--disable", 'd', RTGETOPT_REQ_NOTHING },
193 { "-disable", 'd', RTGETOPT_REQ_NOTHING }, // deprecated
194 { "--global", 'g', RTGETOPT_REQ_NOTHING },
195 { "--vm", 'M', RTGETOPT_REQ_STRING },
196 { "--nic", 'n', RTGETOPT_REQ_UINT8 },
197 { "--add-opt", 'A', RTGETOPT_REQ_STRING },
198 { "--del-opt", 'D', RTGETOPT_REQ_STRING },
199 { "--id", 'i', RTGETOPT_REQ_UINT8 }, // obsolete, backwards compatibility only.
200 { "--value", 'p', RTGETOPT_REQ_STRING }, // obsolete, backwards compatibility only.
201 { "--remove", 'r', RTGETOPT_REQ_NOTHING }, // obsolete, backwards compatibility only.
202 { "--options", 'o', RTGETOPT_REQ_NOTHING }, // obsolete legacy, ignored
203
204 };
205
206 bool fNeedValueOrRemove = false; /* Only used with --id; remove in 6.1+ */
207
208 const char *pszDhcpdIp = NULL;
209 const char *pszNetmask = NULL;
210 const char *pszLowerIp = NULL;
211 const char *pszUpperIp = NULL;
212 int fEnabled = -1;
213
214 DhcpOpts GlobalDhcpOptions;
215 DhcpOptIds GlobalDhcpOptions2Delete;
216 VmSlot2OptionsM VmSlot2Options;
217 VmSlot2OptionIdsM VmSlot2Options2Delete;
218 /// @todo what was this for: VmConfigs VmConfigs2Delete;
219
220 const char *pszVmName = NULL;
221 uint8_t u8OptId = 0;
222 uint8_t u8Slot = 0;
223 DhcpOpts *pScopeOptions = &GlobalDhcpOptions;
224 DhcpOptIds *pScopeOptions2Delete = &GlobalDhcpOptions2Delete;
225
226 int c;
227 RTGETOPTUNION ValueUnion;
228 RTGETOPTSTATE GetState;
229 RTGetOptInit(&GetState,
230 a->argc,
231 a->argv,
232 s_aDHCPIPOptions,
233 enmCode != OP_REMOVE ? RT_ELEMENTS(s_aDHCPIPOptions) : 4, /* we use only --netname and --ifname for remove*/
234 1,
235 0);
236 while ((c = RTGetOpt(&GetState, &ValueUnion)))
237 {
238 switch (c)
239 {
240 DHCPD_CMD_COMMON_OPTION_CASES(pCtx, c, &ValueUnion);
241 case 'a': // -ip
242 pszDhcpdIp = ValueUnion.psz;
243 break;
244 case 'm': // --netmask
245 pszNetmask = ValueUnion.psz;
246 break;
247 case 'l': // --lowerip
248 pszLowerIp = ValueUnion.psz;
249 break;
250 case 'u': // --upperip
251 pszUpperIp = ValueUnion.psz;
252 break;
253 case 'e': // --enable
254 fEnabled = 1;
255 break;
256 case 'd': // --disable
257 fEnabled = 0;
258 break;
259
260 case 'g': // --global Sets the option scope to 'global'.
261 if (fNeedValueOrRemove)
262 return errorSyntax("Incomplete option sequence preseeding '--global'");
263 pScopeOptions = &GlobalDhcpOptions;
264 pScopeOptions2Delete = &GlobalDhcpOptions2Delete;
265 break;
266
267 case 'M': // --vm Sets the option scope to ValueUnion.psz + 0.
268 if (fNeedValueOrRemove)
269 return errorSyntax("Incomplete option sequence preseeding '--vm'");
270 pszVmName = ValueUnion.psz;
271 u8Slot = 0;
272 pScopeOptions = &VmSlot2Options[VmNameSlotKey(pszVmName, u8Slot)];
273 pScopeOptions2Delete = &VmSlot2Options2Delete[VmNameSlotKey(pszVmName, u8Slot)];
274 break;
275
276 case 'n': // --nic Sets the option scope to pszVmName + (ValueUnion.u8 - 1).
277 if (!pszVmName)
278 return errorSyntax("--nic option requires a --vm preceeding selecting the VM it should apply to");
279 if (fNeedValueOrRemove)
280 return errorSyntax("Incomplete option sequence preseeding '--nic=%u", ValueUnion.u8);
281 u8Slot = ValueUnion.u8;
282 if (u8Slot < 1)
283 return errorSyntax("invalid NIC number: %u", u8Slot);
284 --u8Slot;
285 pScopeOptions = &VmSlot2Options[VmNameSlotKey(pszVmName, u8Slot)];
286 pScopeOptions2Delete = &VmSlot2Options2Delete[VmNameSlotKey(pszVmName, u8Slot)];
287 break;
288
289 case 'A': // --add-opt num hexvalue
290 {
291 uint8_t const idAddOpt = ValueUnion.u8;
292 c = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
293 if (RT_FAILURE(c))
294 return errorFetchValue(1, "--add-opt", c, &ValueUnion);
295 pScopeOptions->push_back(DhcpOptSpec((DhcpOpt_T)idAddOpt, std::string(ValueUnion.psz)));
296 break;
297 }
298
299 case 'D': // --del-opt num
300 if (enmCode == OP_ADD)
301 return errorSyntax("--del-opt does not apply to the 'add' subcommand");
302 pScopeOptions2Delete->push_back((DhcpOpt_T)ValueUnion.u8);
303 break;
304
305 /*
306 * For backwards compatibility. Remove in 6.1 or later.
307 */
308
309 case 'o': // --options - obsolete, ignored.
310 break;
311
312 case 'i': // --id
313 if (fNeedValueOrRemove)
314 return errorSyntax("Incomplete option sequence preseeding '--id=%u", ValueUnion.u8);
315 u8OptId = ValueUnion.u8;
316 fNeedValueOrRemove = true;
317 break;
318
319 case 'p': // --value
320 if (!fNeedValueOrRemove)
321 return errorSyntax("--value without --id=dhcp-opt-no");
322 pScopeOptions->push_back(DhcpOptSpec((DhcpOpt_T)u8OptId, std::string(ValueUnion.psz)));
323 fNeedValueOrRemove = false;
324 break;
325
326 case 'r': // --remove
327 if (enmCode == OP_ADD)
328 return errorSyntax("--remove does not apply to the 'add' subcommand");
329 if (!fNeedValueOrRemove)
330 return errorSyntax("--remove without --id=dhcp-opt-no");
331 pScopeOptions2Delete->push_back((DhcpOpt_T)u8OptId);
332 /** @todo remove from pScopeOptions */
333 fNeedValueOrRemove = false;
334 break;
335
336 default:
337 return errorGetOpt(c, &ValueUnion);
338 }
339 }
340
341 if (!pCtx->pszNetwork && !pCtx->pszInterface)
342 return errorSyntax("You need to specify either --network or --interface to identify the DHCP server");
343
344 if ( enmCode != OP_REMOVE
345 && enmCode != OP_RESTART
346 && GlobalDhcpOptions.empty()
347 && VmSlot2Options.empty()
348 && GlobalDhcpOptions2Delete.empty()
349 && VmSlot2Options2Delete.empty())
350 {
351 /** @todo For the 'modify' subcmd, missing configuration parameters could be
352 * retrieved from the current config. All are only required for 'add'!
353 * The 'fEnabled' attribute does not come into play here. */
354 if (fEnabled < 0 || pszDhcpdIp || pszNetmask || pszLowerIp || pszUpperIp)
355 {
356 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
357 if (!pszDhcpdIp)
358 rcExit = errorSyntax("Missing required option: --ip");
359 if (!pszNetmask)
360 rcExit = errorSyntax("Missing required option: --netmask");
361 if (!pszLowerIp)
362 rcExit = errorSyntax("Missing required option: --lowerip");
363 if (!pszUpperIp)
364 rcExit = errorSyntax("Missing required option: --upperip");
365 if (rcExit != RTEXITCODE_SUCCESS)
366 return rcExit;
367 }
368 }
369
370 HRESULT rc;
371 Bstr NetName;
372 if (!pCtx->pszNetwork)
373 {
374 ComPtr<IHost> host;
375 CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
376
377 ComPtr<IHostNetworkInterface> hif;
378 CHECK_ERROR(host, FindHostNetworkInterfaceByName(Bstr(pCtx->pszInterface).mutableRaw(), hif.asOutParam()));
379 if (FAILED(rc))
380 return errorArgument("Could not find interface '%s'", pCtx->pszInterface);
381
382 CHECK_ERROR(hif, COMGETTER(NetworkName) (NetName.asOutParam()));
383 if (FAILED(rc))
384 return errorArgument("Could not get network name for the interface '%s'", pCtx->pszInterface);
385 }
386 else
387 {
388 NetName = Bstr(pCtx->pszNetwork);
389 }
390
391 ComPtr<IDHCPServer> svr;
392 rc = a->virtualBox->FindDHCPServerByNetworkName(NetName.mutableRaw(), svr.asOutParam());
393 if(enmCode == OP_ADD)
394 {
395 if (SUCCEEDED(rc))
396 return errorArgument("DHCP server already exists");
397
398 CHECK_ERROR(a->virtualBox, CreateDHCPServer(NetName.mutableRaw(), svr.asOutParam()));
399 if (FAILED(rc))
400 return errorArgument("Failed to create the DHCP server");
401 }
402 else if (FAILED(rc))
403 {
404 return errorArgument("DHCP server does not exist");
405 }
406
407 if (enmCode == OP_RESTART)
408 {
409 CHECK_ERROR(svr, Restart());
410 if(FAILED(rc))
411 return errorArgument("Failed to restart server");
412 }
413 else if (enmCode == OP_REMOVE)
414 {
415 CHECK_ERROR(a->virtualBox, RemoveDHCPServer(svr));
416 if(FAILED(rc))
417 return errorArgument("Failed to remove server");
418 }
419 else
420 {
421 if (pszDhcpdIp || pszNetmask || pszLowerIp || pszUpperIp)
422 {
423 CHECK_ERROR(svr, SetConfiguration(Bstr(pszDhcpdIp).mutableRaw(),
424 Bstr(pszNetmask).mutableRaw(),
425 Bstr(pszLowerIp).mutableRaw(),
426 Bstr(pszUpperIp).mutableRaw()));
427 if (FAILED(rc))
428 return errorArgument("Failed to set configuration");
429 }
430
431 if (fEnabled >= 0)
432 CHECK_ERROR(svr, COMSETTER(Enabled)((BOOL)fEnabled));
433
434 /* Remove options: */
435 for (DhcpOptIdIterator itOptId = GlobalDhcpOptions2Delete.begin(); itOptId != GlobalDhcpOptions2Delete.end(); ++itOptId)
436 CHECK_ERROR(svr, RemoveGlobalOption(*itOptId));
437
438 for (VmSlot2OptionIdsIterator itIdVector = VmSlot2Options2Delete.begin();
439 itIdVector != VmSlot2Options2Delete.end(); ++itIdVector)
440 for (DhcpOptIdIterator itOptId = itIdVector->second.begin(); itOptId != itIdVector->second.end(); ++itOptId)
441 CHECK_ERROR(svr, RemoveVmSlotOption(Bstr(itIdVector->first.VmName.c_str()).raw(),
442 itIdVector->first.u8Slot, *itOptId));
443
444 /* Global Options */
445 for (DhcpOptIterator itOpt = GlobalDhcpOptions.begin(); itOpt != GlobalDhcpOptions.end(); ++itOpt)
446 CHECK_ERROR(svr, AddGlobalOption(itOpt->first, com::Bstr(itOpt->second.c_str()).raw()));
447
448 /* VM slot options. */
449 for (VmSlot2OptionsIterator it = VmSlot2Options.begin(); it != VmSlot2Options.end(); ++it)
450 for (DhcpOptIterator itOpt = it->second.begin(); itOpt != it->second.end(); ++itOpt)
451 CHECK_ERROR(svr, AddVmSlotOption(Bstr(it->first.VmName.c_str()).raw(), it->first.u8Slot, itOpt->first,
452 com::Bstr(itOpt->second.c_str()).raw()));
453 }
454
455 return RTEXITCODE_SUCCESS;
456}
457
458
459/**
460 * Helper that find the DHCP server instance.
461 *
462 * @returns The DHCP server instance. NULL if failed (complaining done).
463 * @param pCtx The DHCP server command context.
464 */
465static ComPtr<IDHCPServer> dhcpdFindServer(PDHCPDCMDCTX pCtx)
466{
467 ComPtr<IDHCPServer> ptrRet;
468 if (pCtx->pszNetwork || pCtx->pszInterface)
469 {
470 Assert(pCtx->pszNetwork == NULL || pCtx->pszInterface == NULL);
471
472 /*
473 * We need a network name to find the DHCP server. So, if interface is
474 * given we have to look it up.
475 */
476 HRESULT hrc;
477 Bstr bstrNetName(pCtx->pszNetwork);
478 if (!pCtx->pszNetwork)
479 {
480 ComPtr<IHost> ptrIHost;
481 CHECK_ERROR2_RET(hrc, pCtx->pArg->virtualBox, COMGETTER(Host)(ptrIHost.asOutParam()), ptrRet);
482
483 Bstr bstrInterface(pCtx->pszInterface);
484 ComPtr<IHostNetworkInterface> ptrIHostIf;
485 CHECK_ERROR2(hrc, ptrIHost, FindHostNetworkInterfaceByName(bstrInterface.raw(), ptrIHostIf.asOutParam()));
486 if (FAILED(hrc))
487 {
488 errorArgument("Failed to locate host-only interface '%s'", pCtx->pszInterface);
489 return ptrRet;
490 }
491
492 CHECK_ERROR2_RET(hrc, ptrIHostIf, COMGETTER(NetworkName)(bstrNetName.asOutParam()), ptrRet);
493 }
494
495 /*
496 * Now, try locate the server
497 */
498 hrc = pCtx->pArg->virtualBox->FindDHCPServerByNetworkName(bstrNetName.raw(), ptrRet.asOutParam());
499 if (SUCCEEDED(hrc))
500 return ptrRet;
501 if (pCtx->pszNetwork)
502 errorArgument("Failed to find DHCP server for network '%s'", pCtx->pszNetwork);
503 else
504 errorArgument("Failed to find DHCP server for host-only interface '%s' (network '%ls')",
505 pCtx->pszInterface, bstrNetName.raw());
506 }
507 else
508 errorSyntax("You need to specify either --network or --interface to identify the DHCP server");
509 return ptrRet;
510}
511
512
513static RTEXITCODE dhcpdHandleFindLease(PDHCPDCMDCTX pCtx, int argc, char **argv)
514{
515 /*
516 * Parse the command line.
517 */
518 static const RTGETOPTDEF s_aOptions[] =
519 {
520 DHCPD_CMD_COMMON_OPTION_DEFS(),
521 { "--mac-address", 'm', RTGETOPT_REQ_MACADDR },
522
523 };
524
525 bool fHaveMacAddress = false;
526 RTMAC MacAddress = { 0, 0, 0, 0, 0, 0 };
527
528 RTGETOPTSTATE GetState;
529 int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
530 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
531
532 RTGETOPTUNION ValueUnion;
533 while ((vrc = RTGetOpt(&GetState, &ValueUnion)))
534 {
535 switch (vrc)
536 {
537 DHCPD_CMD_COMMON_OPTION_CASES(pCtx, vrc, &ValueUnion);
538
539 case 'm': // --mac-address
540 fHaveMacAddress = true;
541 MacAddress = ValueUnion.MacAddr;
542 break;
543
544 default:
545 return errorGetOpt(vrc, &ValueUnion);
546 }
547 }
548
549 if (!fHaveMacAddress)
550 return errorSyntax("You need to specify a MAC address too look for");
551
552 /*
553 * Locate the server and perform the requested operation.
554 */
555 ComPtr<IDHCPServer> ptrDHCPServer = dhcpdFindServer(pCtx);
556 if (ptrDHCPServer.isNull())
557 return RTEXITCODE_FAILURE;
558
559 char szMac[32];
560 RTStrPrintf(szMac, sizeof(szMac), "%RTmac", &MacAddress);
561 Bstr bstrAddress;
562 Bstr bstrState;
563 LONG64 secIssued = 0;
564 LONG64 secExpire = 0;
565 HRESULT hrc;
566 CHECK_ERROR2(hrc, ptrDHCPServer, FindLeaseByMAC(Bstr(szMac).raw(), 0 /*type*/,
567 bstrAddress.asOutParam(), bstrState.asOutParam(), &secIssued, &secExpire));
568 if (SUCCEEDED(hrc))
569 {
570 RTTIMESPEC TimeSpec;
571 int64_t cSecLeftToLive = secExpire - RTTimeSpecGetSeconds(RTTimeNow(&TimeSpec));
572 RTTIME Time;
573 char szIssued[RTTIME_STR_LEN];
574 RTTimeToStringEx(RTTimeExplode(&Time, RTTimeSpecSetSeconds(&TimeSpec, secIssued)), szIssued, sizeof(szIssued), 0);
575 char szExpire[RTTIME_STR_LEN];
576 RTTimeToStringEx(RTTimeExplode(&Time, RTTimeSpecSetSeconds(&TimeSpec, secExpire)), szExpire, sizeof(szExpire), 0);
577
578 RTPrintf("IP Address: %ls\n"
579 "MAC Address: %RTmac\n"
580 "State: %ls\n"
581 "Issued: %s (%RU64)\n"
582 "Expire: %s (%RU64)\n"
583 "TTL: %RU64 sec, currently %RU64 sec left\n",
584 bstrAddress.raw(),
585 &MacAddress,
586 bstrState.raw(),
587 szIssued, secIssued,
588 szExpire, secExpire,
589 secExpire >= secIssued ? secExpire - secIssued : 0, cSecLeftToLive > 0 ? cSecLeftToLive : 0);
590 return RTEXITCODE_SUCCESS;
591 }
592 return RTEXITCODE_FAILURE;
593}
594
595
596RTEXITCODE handleDHCPServer(HandlerArg *pArg)
597{
598 /*
599 * Command definitions.
600 */
601 static const DHCPDCMDDEF s_aCmdDefs[] =
602 {
603 { "add", dhcpdHandleTooManyCmds, HELP_SCOPE_DHCPSERVER_ADD },
604 { "modify", dhcpdHandleTooManyCmds, HELP_SCOPE_DHCPSERVER_MODIFY },
605 { "remove", dhcpdHandleTooManyCmds, HELP_SCOPE_DHCPSERVER_REMOVE },
606 { "restart", dhcpdHandleTooManyCmds, HELP_SCOPE_DHCPSERVER_RESTART },
607 { "findlease", dhcpdHandleFindLease, HELP_SCOPE_DHCPSERVER_FINDLEASE },
608 };
609
610 /*
611 * VBoxManage dhcpserver [common-options] subcommand ...
612 */
613 DHCPDCMDCTX CmdCtx;
614 CmdCtx.pArg = pArg;
615 CmdCtx.pCmdDef = NULL;
616 CmdCtx.pszInterface = NULL;
617 CmdCtx.pszNetwork = NULL;
618
619 static const RTGETOPTDEF s_CommonOptions[] = { DHCPD_CMD_COMMON_OPTION_DEFS() };
620
621 int ch;
622 RTGETOPTUNION ValueUnion;
623 RTGETOPTSTATE GetState;
624 RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_CommonOptions, RT_ELEMENTS(s_CommonOptions), 0, 0 /* No sorting! */);
625
626 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
627 {
628 switch (ch)
629 {
630 DHCPD_CMD_COMMON_OPTION_CASES(&CmdCtx, ch, &ValueUnion);
631
632 case VINF_GETOPT_NOT_OPTION:
633 {
634 const char *pszCmd = ValueUnion.psz;
635 uint32_t iCmd;
636 for (iCmd = 0; iCmd < RT_ELEMENTS(s_aCmdDefs); iCmd++)
637 if (strcmp(s_aCmdDefs[iCmd].pszName, pszCmd) == 0)
638 {
639 CmdCtx.pCmdDef = &s_aCmdDefs[iCmd];
640 setCurrentSubcommand(s_aCmdDefs[iCmd].fSubcommandScope);
641 return s_aCmdDefs[iCmd].pfnHandler(&CmdCtx, pArg->argc - GetState.iNext + 1,
642 &pArg->argv[GetState.iNext - 1]);
643 }
644 return errorUnknownSubcommand(pszCmd);
645 }
646
647 default:
648 return errorGetOpt(ch, &ValueUnion);
649 }
650 }
651 return errorNoSubcommand();
652}
653
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