VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp@ 29040

Last change on this file since 29040 was 29040, checked in by vboxsync, 15 years ago

VBoxService/PropCache: Finished some todos, IPRT: Added VINF_NO_CHANGE.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.0 KB
Line 
1/* $Id: VBoxServiceVMInfo.cpp 29040 2010-05-04 20:09:03Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host.
4 */
5
6/*
7 * Copyright (C) 2009-2010 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/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#ifdef RT_OS_WINDOWS
24#include <winsock2.h>
25#include <ws2tcpip.h>
26#include <windows.h>
27#include <Ntsecapi.h>
28#else
29# define __STDC_LIMIT_MACROS
30# include <arpa/inet.h>
31# include <errno.h>
32# include <netinet/in.h>
33# include <sys/ioctl.h>
34# include <sys/socket.h>
35# include <net/if.h>
36# include <unistd.h>
37# ifndef RT_OS_FREEBSD /* The header does not exist anymore since FreeBSD 9-current */
38# include <utmp.h>
39# endif
40# ifdef RT_OS_SOLARIS
41# include <sys/sockio.h>
42# endif
43#endif
44
45#include <iprt/mem.h>
46#include <iprt/thread.h>
47#include <iprt/string.h>
48#include <iprt/semaphore.h>
49#include <iprt/system.h>
50#include <iprt/time.h>
51#include <iprt/assert.h>
52#include <VBox/version.h>
53#include <VBox/VBoxGuestLib.h>
54#include "VBoxServiceInternal.h"
55#include "VBoxServiceUtils.h"
56#include "VBoxServicePropCache.h"
57
58
59/*******************************************************************************
60* Global Variables *
61*******************************************************************************/
62/** The vminfo interval (millseconds). */
63static uint32_t g_cMsVMInfoInterval = 0;
64/** The semaphore we're blocking on. */
65static RTSEMEVENTMULTI g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
66/** The guest property service client ID. */
67static uint32_t g_uVMInfoGuestPropSvcClientID = 0;
68/** Number of logged in users in OS. */
69static uint32_t g_cVMInfoLoggedInUsers = UINT32_MAX;
70/** The guest property cache. */
71static VBOXSERVICEVEPROPCACHE g_VMInfoPropCache;
72
73
74/** @copydoc VBOXSERVICE::pfnPreInit */
75static DECLCALLBACK(int) VBoxServiceVMInfoPreInit(void)
76{
77 return VINF_SUCCESS;
78}
79
80
81/** @copydoc VBOXSERVICE::pfnOption */
82static DECLCALLBACK(int) VBoxServiceVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi)
83{
84 int rc = -1;
85 if (ppszShort)
86 /* no short options */;
87 else if (!strcmp(argv[*pi], "--vminfo-interval"))
88 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
89 &g_cMsVMInfoInterval, 1, UINT32_MAX - 1);
90 return rc;
91}
92
93
94/** @copydoc VBOXSERVICE::pfnInit */
95static DECLCALLBACK(int) VBoxServiceVMInfoInit(void)
96{
97 /*
98 * If not specified, find the right interval default.
99 * Then create the event sem to block on.
100 */
101 if (!g_cMsVMInfoInterval)
102 g_cMsVMInfoInterval = g_DefaultInterval * 1000;
103 if (!g_cMsVMInfoInterval)
104 g_cMsVMInfoInterval = 10 * 1000;
105
106 int rc = RTSemEventMultiCreate(&g_hVMInfoEvent);
107 AssertRCReturn(rc, rc);
108
109#ifdef RT_OS_WINDOWS
110 /* Get function pointers. */
111 HMODULE hKernel32 = LoadLibrary("kernel32");
112 if (hKernel32 != NULL)
113 {
114 g_pfnWTSGetActiveConsoleSessionId = (PFNWTSGETACTIVECONSOLESESSIONID)GetProcAddress(hKernel32, "WTSGetActiveConsoleSessionId");
115 FreeLibrary(hKernel32);
116 }
117#endif
118
119 rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID);
120 if (RT_SUCCESS(rc))
121 VBoxServiceVerbose(3, "Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID);
122 else
123 {
124 VBoxServiceError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
125 RTSemEventMultiDestroy(g_hVMInfoEvent);
126 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
127 }
128
129 if (RT_SUCCESS(rc))
130 {
131 VBoxServicePropCacheCreate(&g_VMInfoPropCache, g_uVMInfoGuestPropSvcClientID);
132
133 /** @todo r=bird: Setting Net/Count to 0 here is wrong and will confuse users.
134 * Besides, because it is the beacon updating it implies that all the
135 * other values are up to date too, but they aren't
136 * (VBoxServiceVMInfoWriteFixedProperties hasn't been called yet for
137 * instance). I would suggest changing these statements to
138 * "declarations" or moving them. */
139
140 /*
141 * Initialize some guest properties to have flags and reset values.
142 */
143 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList",
144 VBOXSERVICEPROPCACHEFLAG_TEMPORARY, NULL /* Delete on exit */);
145 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers",
146 VBOXSERVICEPROPCACHEFLAG_TEMPORARY, "0");
147 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers",
148 VBOXSERVICEPROPCACHEFLAG_TEMPORARY, "true");
149 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count",
150 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */);
151 }
152 return rc;
153}
154
155
156/**
157 * Writes the properties that won't change while the service is running.
158 *
159 * Errors are ignored.
160 */
161static void VBoxServiceVMInfoWriteFixedProperties(void)
162{
163 /*
164 * First get OS information that won't change.
165 */
166 char szInfo[256];
167 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szInfo, sizeof(szInfo));
168 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Product",
169 "%s", RT_FAILURE(rc) ? "" : szInfo);
170
171 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szInfo, sizeof(szInfo));
172 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Release",
173 "%s", RT_FAILURE(rc) ? "" : szInfo);
174
175 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szInfo, sizeof(szInfo));
176 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Version",
177 "%s", RT_FAILURE(rc) ? "" : szInfo);
178
179 rc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szInfo, sizeof(szInfo));
180 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/ServicePack",
181 "%s", RT_FAILURE(rc) ? "" : szInfo);
182
183 /*
184 * Retrieve version information about Guest Additions and installed files (components).
185 */
186 char *pszAddVer;
187 char *pszAddRev;
188 rc = VbglR3GetAdditionsVersion(&pszAddVer, &pszAddRev);
189 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Version",
190 "%s", RT_FAILURE(rc) ? "" : pszAddVer);
191 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Revision",
192 "%s", RT_FAILURE(rc) ? "" : pszAddRev);
193 if (RT_SUCCESS(rc))
194 {
195 RTStrFree(pszAddVer);
196 RTStrFree(pszAddRev);
197 }
198
199#ifdef RT_OS_WINDOWS
200 /*
201 * Do windows specific properties.
202 */
203 char *pszInstDir;
204 rc = VbglR3GetAdditionsInstallationPath(&pszInstDir);
205 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/InstallDir",
206 "%s", RT_FAILURE(rc) ? "" : pszInstDir);
207 if (RT_SUCCESS(rc))
208 RTStrFree(pszInstDir);
209
210 VBoxServiceWinGetComponentVersions(g_uVMInfoGuestPropSvcClientID);
211#endif
212}
213
214
215/** @copydoc VBOXSERVICE::pfnWorker */
216DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown)
217{
218 int rc = VINF_SUCCESS;
219
220 /*
221 * Tell the control thread that it can continue
222 * spawning services.
223 */
224 RTThreadUserSignal(RTThreadSelf());
225
226#ifdef RT_OS_WINDOWS
227 /* Required for network information (must be called per thread). */
228 WSADATA wsaData;
229 if (WSAStartup(MAKEWORD(2, 2), &wsaData))
230 VBoxServiceError("WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(WSAGetLastError()));
231#endif /* RT_OS_WINDOWS */
232
233 /*
234 * Write the fixed properties first.
235 */
236 VBoxServiceVMInfoWriteFixedProperties();
237
238 /*
239 * Now enter the loop retrieving runtime data continuously.
240 */
241 unsigned cErrors = 0;
242 for (;;)
243 {
244/** @todo r=bird: split this code up into functions!! */
245 /* Enumerate logged in users. */
246 uint32_t cUsersInList = 0;
247 char szUserList[4096] = {0};
248
249#ifdef RT_OS_WINDOWS
250# ifndef TARGET_NT4
251 PLUID paSessions = NULL;
252 ULONG cSession = 0;
253 NTSTATUS r = 0;
254
255 /* This function can report stale or orphaned interactive logon sessions
256 of already logged off users (especially in Windows 2000). */
257 r = ::LsaEnumerateLogonSessions(&cSession, &paSessions);
258 VBoxServiceVerbose(3, "Users: Found %ld users.\n", cSession);
259 if (r != STATUS_SUCCESS)
260 {
261 VBoxServiceError("LsaEnumerate failed %lu\n", LsaNtStatusToWinError(r));
262 return 1;
263 }
264
265 PVBOXSERVICEVMINFOPROC paProcs;
266 DWORD cProcs;
267 rc = VBoxServiceVMInfoWinProcessesEnumerate(&paProcs, &cProcs);
268 if (RT_SUCCESS(rc))
269 {
270 for (ULONG i = 0; i < cSession; i++)
271 {
272 VBOXSERVICEVMINFOUSER UserInfo;
273 if ( VBoxServiceVMInfoWinIsLoggedIn(&UserInfo, &paSessions[i])
274 && VBoxServiceVMInfoWinSessionHasProcesses(&paSessions[i], paProcs, cProcs))
275 {
276 if (cUsersInList > 0)
277 strcat(szUserList, ",");
278
279 cUsersInList++;
280
281 char *pszTemp;
282 int rc2 = RTUtf16ToUtf8(UserInfo.wszUser, &pszTemp);
283 if (RT_SUCCESS(rc2))
284 {
285 strcat(szUserList, pszTemp);
286 RTMemFree(pszTemp);
287 }
288 else
289 strcat(szUserList, "<string-convertion-error>");
290 }
291 }
292 VBoxServiceVMInfoWinProcessesFree(paProcs);
293 }
294
295 ::LsaFreeReturnBuffer(paSessions);
296# endif /* TARGET_NT4 */
297#elif defined(RT_OS_FREEBSD)
298 /** @todo FreeBSD: Port logged on user info retrival. */
299#elif defined(RT_OS_OS2)
300 /** @todo OS/2: Port logged on (LAN/local/whatever) user info retrival. */
301#else
302 rc = utmpname(UTMP_FILE);
303# ifdef RT_OS_SOLARIS
304 if (rc != 1)
305# else
306 if (rc != 0)
307# endif
308 {
309 VBoxServiceError("Could not set UTMP file! Error: %ld\n", errno);
310 }
311 setutent();
312 utmp *ut_user;
313 while ((ut_user = getutent()))
314 {
315 /* Make sure we don't add user names which are not
316 * part of type USER_PROCESS and don't add same users twice. */
317 if ( ut_user->ut_type == USER_PROCESS
318 && strstr(szUserList, ut_user->ut_user) == NULL)
319 {
320 /** @todo Do we really want to filter out double user names? (Same user logged in twice)
321 * bird: If we do, then we must add checks for buffer overflows here! */
322 /** @todo r=bird: strstr will filtering out users with similar names. For
323 * example: smith, smithson, joesmith and bobsmith */
324 if (cUsersInList > 0)
325 strcat(szUserList, ",");
326 strcat(szUserList, ut_user->ut_user);
327 cUsersInList++;
328 }
329 }
330 endutent();
331#endif /* !RT_OS_WINDOWS */
332
333 if (cUsersInList > 0)
334 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", "%s", szUserList);
335 else
336 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", NULL);
337 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers", "%u", cUsersInList);
338 if ( g_cVMInfoLoggedInUsers != cUsersInList
339 || g_cVMInfoLoggedInUsers == UINT32_MAX)
340 {
341 /*
342 * Update this property ONLY if there is a real change from no users to
343 * users or vice versa. The only exception is that the initialization
344 * forces an update, but only once. This ensures consistent property
345 * settings even if the VM aborted previously.
346 */
347 if (cUsersInList == 0)
348 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers", "true");
349 else if (g_cVMInfoLoggedInUsers == 0)
350 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers", "false");
351 }
352 g_cVMInfoLoggedInUsers = cUsersInList;
353
354 /*
355 * Get network configuration.
356 */
357 /** @todo Throw this code into a separate function/module? */
358 int cInterfaces = 0;
359#ifdef RT_OS_WINDOWS
360 SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
361 if (sd == SOCKET_ERROR) /* Socket invalid. */
362 {
363 VBoxServiceError("Failed to get a socket: Error %d\n", WSAGetLastError());
364 return -1;
365 }
366
367 INTERFACE_INFO InterfaceList[20] = {0};
368 unsigned long nBytesReturned = 0;
369 if (WSAIoctl(sd,
370 SIO_GET_INTERFACE_LIST,
371 0,
372 0,
373 &InterfaceList,
374 sizeof(InterfaceList),
375 &nBytesReturned,
376 0,
377 0) == SOCKET_ERROR)
378 {
379 VBoxServiceError("Failed to WSAIoctl() on socket: Error: %d\n", WSAGetLastError());
380 return -1;
381 }
382 cInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
383#else
384 int sd = socket(AF_INET, SOCK_DGRAM, 0);
385 if (sd < 0) /* Socket invalid. */
386 {
387 VBoxServiceError("Failed to get a socket: Error %d\n", errno);
388 return -1;
389 }
390
391 ifconf ifcfg;
392 char buffer[1024] = {0};
393 ifcfg.ifc_len = sizeof(buffer);
394 ifcfg.ifc_buf = buffer;
395 if (ioctl(sd, SIOCGIFCONF, &ifcfg) < 0)
396 {
397 VBoxServiceError("Failed to ioctl(SIOCGIFCONF) on socket: Error %d\n", errno);
398 return -1;
399 }
400
401 ifreq* ifrequest = ifcfg.ifc_req;
402 ifreq *ifreqitem = NULL;
403 cInterfaces = ifcfg.ifc_len / sizeof(ifreq);
404#endif
405 char szPropPath [FILENAME_MAX];
406 int iCurIface = 0;
407
408 /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
409 for (int i = 0; i < cInterfaces; ++i)
410 {
411 sockaddr_in *pAddress;
412 u_long nFlags = 0;
413#ifdef RT_OS_WINDOWS
414 if (InterfaceList[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
415 continue;
416 nFlags = InterfaceList[i].iiFlags;
417 pAddress = (sockaddr_in *)&(InterfaceList[i].iiAddress);
418#else
419 if (ioctl(sd, SIOCGIFFLAGS, &ifrequest[i]) < 0)
420 {
421 VBoxServiceError("Failed to ioctl(SIOCGIFFLAGS) on socket: Error %d\n", errno);
422 return -1;
423 }
424 if (ifrequest[i].ifr_flags & IFF_LOOPBACK) /* Skip loopback device. */
425 continue;
426 nFlags = ifrequest[i].ifr_flags;
427 pAddress = ((sockaddr_in *)&ifrequest[i].ifr_addr);
428#endif
429 Assert(pAddress);
430 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/IP", iCurIface);
431 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
432
433#ifdef RT_OS_WINDOWS
434 pAddress = (sockaddr_in *) & (InterfaceList[i].iiBroadcastAddress);
435#else
436 if (ioctl(sd, SIOCGIFBRDADDR, &ifrequest[i]) < 0)
437 {
438 VBoxServiceError("Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %d\n", errno);
439 return -1;
440 }
441 pAddress = (sockaddr_in *)&ifrequest[i].ifr_broadaddr;
442#endif
443 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/Broadcast", iCurIface);
444 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
445
446#ifdef RT_OS_WINDOWS
447 pAddress = (sockaddr_in *)&(InterfaceList[i].iiNetmask);
448#else
449 if (ioctl(sd, SIOCGIFNETMASK, &ifrequest[i]) < 0)
450 {
451 VBoxServiceError("Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %d\n", errno);
452 return -1;
453 }
454 #if defined(RT_OS_FREEBSD) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
455 pAddress = (sockaddr_in *)&ifrequest[i].ifr_addr;
456 #else
457 pAddress = (sockaddr_in *)&ifrequest[i].ifr_netmask;
458 #endif
459
460#endif
461 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/Netmask", iCurIface);
462 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
463
464 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/Status", iCurIface);
465 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath,
466 nFlags & IFF_UP ? "Up" : "Down");
467 iCurIface++;
468 }
469 if (sd >= 0)
470#ifdef RT_OS_WINDOWS
471 closesocket(sd);
472#else
473 close(sd);
474#endif
475
476 /*
477 * This property is a beacon which is _always_ written, even if the network configuration
478 * does not change. If this property is missing, the host assumes that all other GuestInfo
479 * properties are no longer valid.
480 *
481 * cInterfaces also counts in local loopback, but we don't want to report that.
482 */
483 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count", "%d",
484 cInterfaces > 1 ? cInterfaces-1 : 0);
485
486 /** @todo r=bird: if cInterfaces decreased compared to the previous run, zap
487 * the stale data. */
488
489
490 /*
491 * Block for a while.
492 *
493 * The event semaphore takes care of ignoring interruptions and it
494 * allows us to implement service wakeup later.
495 */
496 if (*pfShutdown)
497 break;
498 int rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval);
499 if (*pfShutdown)
500 break;
501 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
502 {
503 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
504 rc = rc2;
505 break;
506 }
507 }
508
509#ifdef RT_OS_WINDOWS
510 WSACleanup();
511#endif
512
513 return rc;
514}
515
516
517/** @copydoc VBOXSERVICE::pfnStop */
518static DECLCALLBACK(void) VBoxServiceVMInfoStop(void)
519{
520 RTSemEventMultiSignal(g_hVMInfoEvent);
521}
522
523
524/** @copydoc VBOXSERVICE::pfnTerm */
525static DECLCALLBACK(void) VBoxServiceVMInfoTerm(void)
526{
527 int rc;
528
529 if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
530 {
531 /** @todo temporary solution: Zap all values which are not valid
532 * anymore when VM goes down (reboot/shutdown ). Needs to
533 * be replaced with "temporary properties" later.
534 *
535 * One idea is to introduce a (HGCM-)session guest property
536 * flag meaning that a guest property is only valid as long
537 * as the HGCM session isn't closed (e.g. guest application
538 * terminates). [don't remove till implemented]
539 */
540 /** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and make the cache
541 * remember what we've written. */
542 /* Delete the "../Net" branch. */
543 const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
544 rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
545
546 /* Destroy property cache. */
547 VBoxServicePropCacheDestroy(&g_VMInfoPropCache);
548
549 /* Disconnect from guest properties service. */
550 rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID);
551 if (RT_FAILURE(rc))
552 VBoxServiceError("Failed to disconnect from guest property service! Error: %Rrc\n", rc);
553 g_uVMInfoGuestPropSvcClientID = 0;
554
555 RTSemEventMultiDestroy(g_hVMInfoEvent);
556 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
557 }
558}
559
560
561/**
562 * The 'vminfo' service description.
563 */
564VBOXSERVICE g_VMInfo =
565{
566 /* pszName. */
567 "vminfo",
568 /* pszDescription. */
569 "Virtual Machine Information",
570 /* pszUsage. */
571 "[--vminfo-interval <ms>]"
572 ,
573 /* pszOptions. */
574 " --vminfo-interval Specifies the interval at which to retrieve the\n"
575 " VM information. The default is 10000 ms.\n"
576 ,
577 /* methods */
578 VBoxServiceVMInfoPreInit,
579 VBoxServiceVMInfoOption,
580 VBoxServiceVMInfoInit,
581 VBoxServiceVMInfoWorker,
582 VBoxServiceVMInfoStop,
583 VBoxServiceVMInfoTerm
584};
585
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