VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModAPIMonitor.cpp

Last change on this file was 98103, checked in by vboxsync, 18 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.9 KB
Line 
1/* $Id: VBoxModAPIMonitor.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxModAPIMonitor - API monitor module for detecting host isolation.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#ifndef VBOX_ONLY_DOCS
33# include <iprt/message.h>
34# include <VBox/com/errorprint.h>
35#endif /* !VBOX_ONLY_DOCS */
36
37#include "VBoxWatchdogInternal.h"
38
39using namespace com;
40
41#define VBOX_MOD_APIMON_NAME "apimon"
42
43/**
44 * The module's RTGetOpt-IDs for the command line.
45 */
46enum GETOPTDEF_APIMON
47{
48 GETOPTDEF_APIMON_GROUPS = 3000,
49 GETOPTDEF_APIMON_ISLN_RESPONSE,
50 GETOPTDEF_APIMON_ISLN_TIMEOUT,
51 GETOPTDEF_APIMON_RESP_TIMEOUT
52};
53
54/**
55 * The module's command line arguments.
56 */
57static const RTGETOPTDEF g_aAPIMonitorOpts[] = {
58 { "--apimon-groups", GETOPTDEF_APIMON_GROUPS, RTGETOPT_REQ_STRING },
59 { "--apimon-isln-response", GETOPTDEF_APIMON_ISLN_RESPONSE, RTGETOPT_REQ_STRING },
60 { "--apimon-isln-timeout", GETOPTDEF_APIMON_ISLN_TIMEOUT, RTGETOPT_REQ_UINT32 },
61 { "--apimon-resp-timeout", GETOPTDEF_APIMON_RESP_TIMEOUT, RTGETOPT_REQ_UINT32 }
62};
63
64enum APIMON_RESPONSE
65{
66 /** Unknown / unhandled response. */
67 APIMON_RESPONSE_NONE = 0,
68 /** Pauses the VM execution. */
69 APIMON_RESPONSE_PAUSE = 10,
70 /** Does a hard power off. */
71 APIMON_RESPONSE_POWEROFF = 200,
72 /** Tries to save the current machine state. */
73 APIMON_RESPONSE_SAVE = 250,
74 /** Tries to shut down all running VMs in
75 * a gentle manner. */
76 APIMON_RESPONSE_SHUTDOWN = 300
77};
78
79/** The VM group(s) the API monitor handles. If none, all VMs get handled. */
80static mapGroups g_vecAPIMonGroups; /** @todo Move this into module payload! */
81static APIMON_RESPONSE g_enmAPIMonIslnResp = APIMON_RESPONSE_NONE;
82static uint32_t g_cMsAPIMonIslnTimeout = 0;
83static Bstr g_strAPIMonIslnLastBeat;
84static uint32_t g_cMsAPIMonResponseTimeout = 0;
85static uint64_t g_uAPIMonIslnLastBeatMS = 0;
86
87static int apimonResponseToEnum(const char *pszResponse, APIMON_RESPONSE *pResp)
88{
89 AssertPtrReturn(pszResponse, VERR_INVALID_POINTER);
90 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
91
92 int rc = VINF_SUCCESS;
93 if (!RTStrICmp(pszResponse, "none"))
94 {
95 *pResp = APIMON_RESPONSE_NONE;
96 }
97 else if (!RTStrICmp(pszResponse, "pause"))
98 {
99 *pResp = APIMON_RESPONSE_PAUSE;
100 }
101 else if ( !RTStrICmp(pszResponse, "poweroff")
102 || !RTStrICmp(pszResponse, "powerdown"))
103 {
104 *pResp = APIMON_RESPONSE_POWEROFF;
105 }
106 else if (!RTStrICmp(pszResponse, "save"))
107 {
108 *pResp = APIMON_RESPONSE_SAVE;
109 }
110 else if ( !RTStrICmp(pszResponse, "shutdown")
111 || !RTStrICmp(pszResponse, "shutoff"))
112 {
113 *pResp = APIMON_RESPONSE_SHUTDOWN;
114 }
115 else
116 {
117 *pResp = APIMON_RESPONSE_NONE;
118 rc = VERR_INVALID_PARAMETER;
119 }
120
121 return rc;
122}
123
124static const char* apimonResponseToStr(APIMON_RESPONSE enmResp)
125{
126 if (APIMON_RESPONSE_NONE == enmResp)
127 return "none";
128 else if (APIMON_RESPONSE_PAUSE == enmResp)
129 return "pausing";
130 else if (APIMON_RESPONSE_POWEROFF == enmResp)
131 return "powering off";
132 else if (APIMON_RESPONSE_SAVE == enmResp)
133 return "saving state";
134 else if (APIMON_RESPONSE_SHUTDOWN == enmResp)
135 return "shutting down";
136
137 return "unknown";
138}
139
140/* Copied from VBoxManageInfo.cpp. */
141static const char *apimonMachineStateToName(MachineState_T machineState, bool fShort)
142{
143 switch (machineState)
144 {
145 case MachineState_PoweredOff:
146 return fShort ? "poweroff" : "powered off";
147 case MachineState_Saved:
148 return "saved";
149 case MachineState_Teleported:
150 return "teleported";
151 case MachineState_Aborted:
152 return "aborted";
153 case MachineState_AbortedSaved:
154 return "aborted-saved";
155 case MachineState_Running:
156 return "running";
157 case MachineState_Paused:
158 return "paused";
159 case MachineState_Stuck:
160 return fShort ? "gurumeditation" : "guru meditation";
161 case MachineState_LiveSnapshotting:
162 return fShort ? "livesnapshotting" : "live snapshotting";
163 case MachineState_Teleporting:
164 return "teleporting";
165 case MachineState_Starting:
166 return "starting";
167 case MachineState_Stopping:
168 return "stopping";
169 case MachineState_Saving:
170 return "saving";
171 case MachineState_Restoring:
172 return "restoring";
173 case MachineState_TeleportingPausedVM:
174 return fShort ? "teleportingpausedvm" : "teleporting paused vm";
175 case MachineState_TeleportingIn:
176 return fShort ? "teleportingin" : "teleporting (incoming)";
177 case MachineState_RestoringSnapshot:
178 return fShort ? "restoringsnapshot" : "restoring snapshot";
179 case MachineState_DeletingSnapshot:
180 return fShort ? "deletingsnapshot" : "deleting snapshot";
181 case MachineState_DeletingSnapshotOnline:
182 return fShort ? "deletingsnapshotlive" : "deleting snapshot live";
183 case MachineState_DeletingSnapshotPaused:
184 return fShort ? "deletingsnapshotlivepaused" : "deleting snapshot live paused";
185 case MachineState_SettingUp:
186 return fShort ? "settingup" : "setting up";
187 default:
188 break;
189 }
190 return "unknown";
191}
192
193static int apimonMachineControl(const Bstr &strUuid, PVBOXWATCHDOG_MACHINE pMachine,
194 APIMON_RESPONSE enmResp, uint32_t cMsTimeout)
195{
196 /** @todo Add other commands (with enmResp) here. */
197 AssertPtrReturn(pMachine, VERR_INVALID_POINTER);
198
199 serviceLogVerbose(("apimon: Triggering \"%s\" (%RU32ms timeout) for machine \"%ls\"\n",
200 apimonResponseToStr(enmResp), cMsTimeout, strUuid.raw()));
201
202 if ( enmResp == APIMON_RESPONSE_NONE
203 || g_fDryrun)
204 return VINF_SUCCESS; /* Nothing to do. */
205
206 HRESULT hrc;
207 ComPtr <IMachine> machine;
208 CHECK_ERROR_RET(g_pVirtualBox, FindMachine(strUuid.raw(),
209 machine.asOutParam()), VERR_NOT_FOUND);
210 do
211 {
212 /* Query the machine's state to avoid unnecessary IPC. */
213 MachineState_T machineState;
214 CHECK_ERROR_BREAK(machine, COMGETTER(State)(&machineState));
215
216 if ( machineState == MachineState_Running
217 || machineState == MachineState_Paused)
218 {
219 /* Open a session for the VM. */
220 CHECK_ERROR_BREAK(machine, LockMachine(g_pSession, LockType_Shared));
221
222 do
223 {
224 /* Get the associated console. */
225 ComPtr<IConsole> console;
226 CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
227 /* Get the associated session machine. */
228 ComPtr<IMachine> sessionMachine;
229 CHECK_ERROR_BREAK(g_pSession, COMGETTER(Machine)(sessionMachine.asOutParam()));
230
231 ComPtr<IProgress> progress;
232
233 switch (enmResp)
234 {
235 case APIMON_RESPONSE_PAUSE:
236 if (machineState != MachineState_Paused)
237 {
238 serviceLogVerbose(("apimon: Pausing machine \"%ls\" ...\n",
239 strUuid.raw()));
240 CHECK_ERROR_BREAK(console, Pause());
241 }
242 break;
243
244 case APIMON_RESPONSE_POWEROFF:
245 serviceLogVerbose(("apimon: Powering off machine \"%ls\" ...\n",
246 strUuid.raw()));
247 CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));
248 progress->WaitForCompletion(cMsTimeout);
249 CHECK_PROGRESS_ERROR(progress, ("Failed to power off machine \"%ls\"",
250 strUuid.raw()));
251 break;
252
253 case APIMON_RESPONSE_SAVE:
254 {
255 serviceLogVerbose(("apimon: Saving state of machine \"%ls\" ...\n",
256 strUuid.raw()));
257
258 /* First pause so we don't trigger a live save which needs more time/resources. */
259 bool fPaused = false;
260 hrc = console->Pause();
261 if (FAILED(hrc))
262 {
263 bool fError = true;
264 if (hrc == VBOX_E_INVALID_VM_STATE)
265 {
266 /* Check if we are already paused. */
267 CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
268 /* The error code was lost by the previous instruction. */
269 hrc = VBOX_E_INVALID_VM_STATE;
270 if (machineState != MachineState_Paused)
271 {
272 serviceLog("apimon: Machine \"%ls\" in invalid state %d -- %s\n",
273 strUuid.raw(), machineState, apimonMachineStateToName(machineState, false));
274 }
275 else
276 {
277 fError = false;
278 fPaused = true;
279 }
280 }
281 if (fError)
282 break;
283 }
284
285 CHECK_ERROR(sessionMachine, SaveState(progress.asOutParam()));
286 if (SUCCEEDED(hrc))
287 {
288 progress->WaitForCompletion(cMsTimeout);
289 CHECK_PROGRESS_ERROR(progress, ("Failed to save machine state of machine \"%ls\"",
290 strUuid.raw()));
291 }
292
293 if (SUCCEEDED(hrc))
294 {
295 serviceLogVerbose(("apimon: State of machine \"%ls\" saved, powering off ...\n", strUuid.raw()));
296 CHECK_ERROR_BREAK(console, PowerButton());
297 }
298 else
299 serviceLogVerbose(("apimon: Saving state of machine \"%ls\" failed\n", strUuid.raw()));
300
301 break;
302 }
303
304 case APIMON_RESPONSE_SHUTDOWN:
305 serviceLogVerbose(("apimon: Shutting down machine \"%ls\" ...\n", strUuid.raw()));
306 CHECK_ERROR_BREAK(console, PowerButton());
307 break;
308
309 default:
310 AssertMsgFailed(("Response %d not implemented", enmResp));
311 break;
312 }
313 } while (0);
314
315 /* Unlock the machine again. */
316 g_pSession->UnlockMachine();
317 }
318 else
319 serviceLogVerbose(("apimon: Warning: Could not trigger \"%s\" (%d) for machine \"%ls\"; in state \"%s\" (%d) currently\n",
320 apimonResponseToStr(enmResp), enmResp, strUuid.raw(),
321 apimonMachineStateToName(machineState, false), machineState));
322 } while (0);
323
324
325
326 return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_COM_IPRT_ERROR;
327}
328
329static bool apimonHandleVM(const PVBOXWATCHDOG_MACHINE pMachine)
330{
331 bool fHandleVM = false;
332
333 try
334 {
335 mapGroupsIterConst itVMGroup = pMachine->groups.begin();
336 while ( itVMGroup != pMachine->groups.end()
337 && !fHandleVM)
338 {
339 mapGroupsIterConst itInGroup = g_vecAPIMonGroups.find(itVMGroup->first);
340 if (itInGroup != g_vecAPIMonGroups.end())
341 fHandleVM = true;
342
343 ++itVMGroup;
344 }
345 }
346 catch (...)
347 {
348 AssertFailed();
349 }
350
351 return fHandleVM;
352}
353
354static int apimonTrigger(APIMON_RESPONSE enmResp)
355{
356 int rc = VINF_SUCCESS;
357
358 bool fAllGroups = g_vecAPIMonGroups.empty();
359 mapVMIter it = g_mapVM.begin();
360
361 if (it == g_mapVM.end())
362 {
363 serviceLog("apimon: No machines in list, skipping ...\n");
364 return rc;
365 }
366
367 while (it != g_mapVM.end())
368 {
369 bool fHandleVM = fAllGroups;
370 try
371 {
372 if (!fHandleVM)
373 fHandleVM = apimonHandleVM(&it->second);
374
375 if (fHandleVM)
376 {
377 int rc2 = apimonMachineControl(it->first /* Uuid */,
378 &it->second /* Machine */, enmResp, g_cMsAPIMonResponseTimeout);
379 if (RT_FAILURE(rc2))
380 serviceLog("apimon: Controlling machine \"%ls\" (response \"%s\") failed with rc=%Rrc",
381 it->first.raw(), apimonResponseToStr(enmResp), rc);
382
383 if (RT_SUCCESS(rc))
384 rc = rc2; /* Store original error. */
385 /* Keep going. */
386 }
387 }
388 catch (...)
389 {
390 AssertFailed();
391 }
392
393 ++it;
394 }
395
396 return rc;
397}
398
399/* Callbacks. */
400static DECLCALLBACK(int) VBoxModAPIMonitorPreInit(void)
401{
402 return VINF_SUCCESS;
403}
404
405static DECLCALLBACK(int) VBoxModAPIMonitorOption(int argc, char *argv[], int *piConsumed)
406{
407 if (!argc) /* Take a shortcut. */
408 return -1;
409
410 AssertPtrReturn(argv, VERR_INVALID_POINTER);
411 AssertPtrReturn(piConsumed, VERR_INVALID_POINTER);
412
413 RTGETOPTSTATE GetState;
414 int rc = RTGetOptInit(&GetState, argc, argv,
415 g_aAPIMonitorOpts, RT_ELEMENTS(g_aAPIMonitorOpts),
416 0 /* First */, 0 /*fFlags*/);
417 if (RT_FAILURE(rc))
418 return rc;
419
420 rc = 0; /* Set default parsing result to valid. */
421
422 int c;
423 RTGETOPTUNION ValueUnion;
424 while ((c = RTGetOpt(&GetState, &ValueUnion)))
425 {
426 switch (c)
427 {
428 case GETOPTDEF_APIMON_GROUPS:
429 {
430 rc = groupAdd(g_vecAPIMonGroups, ValueUnion.psz, 0 /* Flags */);
431 if (RT_FAILURE(rc))
432 rc = -1; /* Option unknown. */
433 break;
434 }
435
436 case GETOPTDEF_APIMON_ISLN_RESPONSE:
437 rc = apimonResponseToEnum(ValueUnion.psz, &g_enmAPIMonIslnResp);
438 if (RT_FAILURE(rc))
439 rc = -1; /* Option unknown. */
440 break;
441
442 case GETOPTDEF_APIMON_ISLN_TIMEOUT:
443 g_cMsAPIMonIslnTimeout = ValueUnion.u32;
444 if (g_cMsAPIMonIslnTimeout < 1000) /* Don't allow timeouts < 1s. */
445 g_cMsAPIMonIslnTimeout = 1000;
446 break;
447
448 case GETOPTDEF_APIMON_RESP_TIMEOUT:
449 g_cMsAPIMonResponseTimeout = ValueUnion.u32;
450 if (g_cMsAPIMonResponseTimeout < 5000) /* Don't allow timeouts < 5s. */
451 g_cMsAPIMonResponseTimeout = 5000;
452 break;
453
454 default:
455 rc = -1; /* We don't handle this option, skip. */
456 break;
457 }
458
459 /* At the moment we only process one option at a time. */
460 break;
461 }
462
463 *piConsumed += GetState.iNext - 1;
464
465 return rc;
466}
467
468static DECLCALLBACK(int) VBoxModAPIMonitorInit(void)
469{
470 HRESULT hrc = S_OK;
471
472 do
473 {
474 Bstr strValue;
475
476 /* VM groups to watch for. */
477 if (g_vecAPIMonGroups.empty()) /* Not set by command line? */
478 {
479 CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("VBoxInternal2/Watchdog/APIMonitor/Groups").raw(),
480 strValue.asOutParam()));
481 if (!strValue.isEmpty())
482 {
483 int rc2 = groupAdd(g_vecAPIMonGroups, Utf8Str(strValue).c_str(), 0 /* Flags */);
484 if (RT_FAILURE(rc2))
485 serviceLog("apimon: Warning: API monitor groups string invalid (%ls)\n", strValue.raw());
486 }
487 }
488
489 if (!g_cMsAPIMonIslnTimeout)
490 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
491 "VBoxInternal2/Watchdog/APIMonitor/IsolationTimeoutMS", NULL /* Per-machine */,
492 &g_cMsAPIMonIslnTimeout, 30 * 1000 /* Default is 30 seconds timeout. */);
493 g_cMsAPIMonIslnTimeout = RT_MIN(1000, g_cMsAPIMonIslnTimeout);
494
495 if (g_enmAPIMonIslnResp == APIMON_RESPONSE_NONE) /* Not set by command line? */
496 {
497 Utf8Str strResp;
498 int rc2 = cfgGetValueStr(g_pVirtualBox, NULL /* Machine */,
499 "VBoxInternal2/Watchdog/APIMonitor/IsolationResponse", NULL /* Per-machine */,
500 strResp, "" /* Default value. */);
501 if (RT_SUCCESS(rc2))
502 {
503 rc2 = apimonResponseToEnum(strResp.c_str(), &g_enmAPIMonIslnResp);
504 if (RT_FAILURE(rc2))
505 serviceLog("apimon: Warning: API monitor response string invalid (%ls), defaulting to no action\n",
506 strValue.raw());
507 }
508 }
509
510 if (!g_cMsAPIMonResponseTimeout)
511 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
512 "VBoxInternal2/Watchdog/APIMonitor/ResponseTimeoutMS", NULL /* Per-machine */,
513 &g_cMsAPIMonResponseTimeout, 30 * 1000 /* Default is 30 seconds timeout. */);
514 g_cMsAPIMonResponseTimeout = RT_MIN(5000, g_cMsAPIMonResponseTimeout);
515
516#ifdef DEBUG
517 /* Groups. */
518 serviceLogVerbose(("apimon: Handling %u groups:", g_vecAPIMonGroups.size()));
519 mapGroupsIterConst itGroups = g_vecAPIMonGroups.begin();
520 while (itGroups != g_vecAPIMonGroups.end())
521 {
522 serviceLogVerbose((" %s", itGroups->first.c_str()));
523 ++itGroups;
524 }
525 serviceLogVerbose(("\n"));
526#endif
527
528 } while (0);
529
530 if (SUCCEEDED(hrc))
531 {
532 g_uAPIMonIslnLastBeatMS = 0;
533 }
534
535 return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_COM_IPRT_ERROR; /** @todo Find a better rc! */
536}
537
538static DECLCALLBACK(int) VBoxModAPIMonitorMain(void)
539{
540 static uint64_t uLastRun = 0;
541 uint64_t uNow = RTTimeProgramMilliTS();
542 uint64_t uDelta = uNow - uLastRun;
543 if (uDelta < 1000) /* Only check every second (or later). */
544 return VINF_SUCCESS;
545 uLastRun = uNow;
546
547 int vrc = VINF_SUCCESS;
548 HRESULT hrc;
549
550#ifdef DEBUG
551 serviceLogVerbose(("apimon: Checking for API heartbeat (%RU64ms) ...\n",
552 g_cMsAPIMonIslnTimeout));
553#endif
554
555 do
556 {
557 Bstr strHeartbeat;
558 CHECK_ERROR_BREAK(g_pVirtualBox, GetExtraData(Bstr("Watchdog/APIMonitor/Heartbeat").raw(),
559 strHeartbeat.asOutParam()));
560 if ( SUCCEEDED(hrc)
561 && !strHeartbeat.isEmpty()
562 && g_strAPIMonIslnLastBeat.compare(strHeartbeat, Bstr::CaseSensitive))
563 {
564 serviceLogVerbose(("apimon: API heartbeat received, resetting timeout\n"));
565
566 g_uAPIMonIslnLastBeatMS = 0;
567 g_strAPIMonIslnLastBeat = strHeartbeat;
568 }
569 else
570 {
571 g_uAPIMonIslnLastBeatMS += uDelta;
572 if (g_uAPIMonIslnLastBeatMS > g_cMsAPIMonIslnTimeout)
573 {
574 serviceLogVerbose(("apimon: No API heartbeat within time received (%RU64ms)\n",
575 g_cMsAPIMonIslnTimeout));
576
577 vrc = apimonTrigger(g_enmAPIMonIslnResp);
578 g_uAPIMonIslnLastBeatMS = 0;
579 }
580 }
581 } while (0);
582
583 if (FAILED(hrc))
584 vrc = VERR_COM_IPRT_ERROR;
585
586 return vrc;
587}
588
589static DECLCALLBACK(int) VBoxModAPIMonitorStop(void)
590{
591 return VINF_SUCCESS;
592}
593
594static DECLCALLBACK(void) VBoxModAPIMonitorTerm(void)
595{
596}
597
598static DECLCALLBACK(int) VBoxModAPIMonitorOnMachineRegistered(const Bstr &strUuid)
599{
600 RT_NOREF(strUuid);
601 return VINF_SUCCESS;
602}
603
604static DECLCALLBACK(int) VBoxModAPIMonitorOnMachineUnregistered(const Bstr &strUuid)
605{
606 RT_NOREF(strUuid);
607 return VINF_SUCCESS;
608}
609
610static DECLCALLBACK(int) VBoxModAPIMonitorOnMachineStateChanged(const Bstr &strUuid, MachineState_T enmState)
611{
612 RT_NOREF(strUuid, enmState);
613 return VINF_SUCCESS;
614}
615
616static DECLCALLBACK(int) VBoxModAPIMonitorOnServiceStateChanged(bool fAvailable)
617{
618 if (!fAvailable)
619 {
620 serviceLog(("apimon: VBoxSVC became unavailable, triggering action\n"));
621 return apimonTrigger(g_enmAPIMonIslnResp);
622 }
623 return VINF_SUCCESS;
624}
625
626/**
627 * The 'apimonitor' module description.
628 */
629VBOXMODULE g_ModAPIMonitor =
630{
631 /* pszName. */
632 VBOX_MOD_APIMON_NAME,
633 /* pszDescription. */
634 "API monitor for host isolation detection",
635 /* pszDepends. */
636 NULL,
637 /* uPriority. */
638 0 /* Not used */,
639 /* pszUsage. */
640 " [--apimon-groups=<string[,stringN]>]\n"
641 " [--apimon-isln-response=<cmd>] [--apimon-isln-timeout=<ms>]\n"
642 " [--apimon-resp-timeout=<ms>]",
643 /* pszOptions. */
644 " --apimon-groups=<string[,...]>\n"
645 " Sets the VM groups for monitoring (all), comma-separated list.\n"
646 " --apimon-isln-response=<cmd>\n"
647 " Sets the isolation response to one of: none, pause, poweroff,\n"
648 " save, or shutdown. Default: none\n"
649 " --apimon-isln-timeout=<ms>\n"
650 " Sets the isolation timeout in ms (30s).\n"
651 " --apimon-resp-timeout=<ms>\n"
652 " Sets the response timeout in ms (30s).\n",
653 /* methods. */
654 VBoxModAPIMonitorPreInit,
655 VBoxModAPIMonitorOption,
656 VBoxModAPIMonitorInit,
657 VBoxModAPIMonitorMain,
658 VBoxModAPIMonitorStop,
659 VBoxModAPIMonitorTerm,
660 /* callbacks. */
661 VBoxModAPIMonitorOnMachineRegistered,
662 VBoxModAPIMonitorOnMachineUnregistered,
663 VBoxModAPIMonitorOnMachineStateChanged,
664 VBoxModAPIMonitorOnServiceStateChanged
665};
666
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use