VirtualBox

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

Last change on this file since 39106 was 39106, checked in by vboxsync, 13 years ago

VBoxService/VMInfo: Hook CTRL_LOGOFF_EVENT to re-trigger user enumeration on Windows guests to get immediate disconnects from VRDP when a user has been logged out.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.4 KB
Line 
1/* $Id: VBoxServiceVMInfo.cpp 39106 2011-10-25 09:45:40Z 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 <iphlpapi.h>
26# include <ws2tcpip.h>
27# include <windows.h>
28# include <Ntsecapi.h>
29#else
30# define __STDC_LIMIT_MACROS
31# include <arpa/inet.h>
32# include <errno.h>
33# include <netinet/in.h>
34# include <sys/ioctl.h>
35# include <sys/socket.h>
36# include <net/if.h>
37# include <unistd.h>
38# ifndef RT_OS_OS2
39# ifndef RT_OS_FREEBSD
40# include <utmpx.h> /* @todo FreeBSD 9 should have this. */
41# endif
42# endif
43# ifdef RT_OS_SOLARIS
44# include <sys/sockio.h>
45# include <net/if_arp.h>
46# endif
47# ifdef RT_OS_FREEBSD
48# include <ifaddrs.h> /* getifaddrs, freeifaddrs */
49# include <net/if_dl.h> /* LLADDR */
50# include <netdb.h> /* getnameinfo */
51# endif
52#endif
53
54#include <iprt/mem.h>
55#include <iprt/thread.h>
56#include <iprt/string.h>
57#include <iprt/semaphore.h>
58#include <iprt/system.h>
59#include <iprt/time.h>
60#include <iprt/assert.h>
61#include <VBox/version.h>
62#include <VBox/VBoxGuestLib.h>
63#include "VBoxServiceInternal.h"
64#include "VBoxServiceUtils.h"
65#include "VBoxServicePropCache.h"
66
67
68/*******************************************************************************
69* Global Variables *
70*******************************************************************************/
71/** The vminfo interval (milliseconds). */
72static uint32_t g_cMsVMInfoInterval = 0;
73/** The semaphore we're blocking on. */
74static RTSEMEVENTMULTI g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
75/** The guest property service client ID. */
76static uint32_t g_uVMInfoGuestPropSvcClientID = 0;
77/** Number of logged in users in OS. */
78static uint32_t g_cVMInfoLoggedInUsers = UINT32_MAX;
79/** The guest property cache. */
80static VBOXSERVICEVEPROPCACHE g_VMInfoPropCache;
81/** The VM session ID. Changes whenever the VM is restored or reset. */
82static uint64_t g_idVMInfoSession;
83
84
85#ifdef RT_OS_WINDOWS
86static BOOL WINAPI VBoxServiceVMInfoConsoleControlHandler(DWORD dwCtrlType)
87{
88 int rc = VINF_SUCCESS;
89 bool fEventHandled = FALSE;
90 switch (dwCtrlType)
91 {
92 case CTRL_LOGOFF_EVENT:
93 VBoxServiceVerbose(2, "VMInfo: Received logged-off event\n");
94 /* Trigger a re-enumeration of all logged-in users by unblocking
95 * the multi event semaphore of the VMInfo thread. */
96 if (g_hVMInfoEvent)
97 rc = RTSemEventMultiSignal(g_hVMInfoEvent);
98 fEventHandled = TRUE;
99 break;
100 default:
101 break;
102 /** @todo Add other events here. */
103 }
104
105 if (RT_FAILURE(rc))
106 VBoxServiceError("VMInfo: Event %ld handled with error rc=%Rrc\n",
107 dwCtrlType, rc);
108 return fEventHandled;
109}
110#endif /* RT_OS_WINDOWS */
111
112
113/** @copydoc VBOXSERVICE::pfnPreInit */
114static DECLCALLBACK(int) VBoxServiceVMInfoPreInit(void)
115{
116 return VINF_SUCCESS;
117}
118
119
120/** @copydoc VBOXSERVICE::pfnOption */
121static DECLCALLBACK(int) VBoxServiceVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi)
122{
123 int rc = -1;
124 if (ppszShort)
125 /* no short options */;
126 else if (!strcmp(argv[*pi], "--vminfo-interval"))
127 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
128 &g_cMsVMInfoInterval, 1, UINT32_MAX - 1);
129 return rc;
130}
131
132
133/** @copydoc VBOXSERVICE::pfnInit */
134static DECLCALLBACK(int) VBoxServiceVMInfoInit(void)
135{
136 /*
137 * If not specified, find the right interval default.
138 * Then create the event sem to block on.
139 */
140 if (!g_cMsVMInfoInterval)
141 g_cMsVMInfoInterval = g_DefaultInterval * 1000;
142 if (!g_cMsVMInfoInterval)
143 g_cMsVMInfoInterval = 10 * 1000;
144
145 int rc = RTSemEventMultiCreate(&g_hVMInfoEvent);
146 AssertRCReturn(rc, rc);
147
148 VbglR3GetSessionId(&g_idVMInfoSession);
149 /* The status code is ignored as this information is not available with VBox < 3.2.10. */
150
151 rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID);
152 if (RT_SUCCESS(rc))
153 VBoxServiceVerbose(3, "VMInfo: Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID);
154 else
155 {
156 /* If the service was not found, we disable this service without
157 causing VBoxService to fail. */
158 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
159 {
160 VBoxServiceVerbose(0, "VMInfo: Guest property service is not available, disabling the service\n");
161 rc = VERR_SERVICE_DISABLED;
162 }
163 else
164 VBoxServiceError("VMInfo: Failed to connect to the guest property service! Error: %Rrc\n", rc);
165 RTSemEventMultiDestroy(g_hVMInfoEvent);
166 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
167 }
168
169 if (RT_SUCCESS(rc))
170 {
171 VBoxServicePropCacheCreate(&g_VMInfoPropCache, g_uVMInfoGuestPropSvcClientID);
172
173 /*
174 * Declare some guest properties with flags and reset values.
175 */
176 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList",
177 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, NULL /* Delete on exit */);
178 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers",
179 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "0");
180 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers",
181 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "true");
182 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count",
183 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */);
184
185#ifdef RT_OS_WINDOWS
186# ifndef RT_OS_NT4
187 /* Install console control handler. */
188 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)VBoxServiceVMInfoConsoleControlHandler, TRUE /* Add handler */))
189 {
190 VBoxServiceError("VMInfo: Unable to add console control handler, error=%ld\n", GetLastError());
191 /* Just skip this error, not critical. */
192 }
193# endif /* !RT_OS_NT4 */
194#endif /* RT_OS_WINDOWS */
195 }
196 return rc;
197}
198
199
200/**
201 * Writes the properties that won't change while the service is running.
202 *
203 * Errors are ignored.
204 */
205static void vboxserviceVMInfoWriteFixedProperties(void)
206{
207 /*
208 * First get OS information that won't change.
209 */
210 char szInfo[256];
211 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szInfo, sizeof(szInfo));
212 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Product",
213 "%s", RT_FAILURE(rc) ? "" : szInfo);
214
215 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szInfo, sizeof(szInfo));
216 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Release",
217 "%s", RT_FAILURE(rc) ? "" : szInfo);
218
219 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szInfo, sizeof(szInfo));
220 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Version",
221 "%s", RT_FAILURE(rc) ? "" : szInfo);
222
223 rc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szInfo, sizeof(szInfo));
224 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/ServicePack",
225 "%s", RT_FAILURE(rc) ? "" : szInfo);
226
227 /*
228 * Retrieve version information about Guest Additions and installed files (components).
229 */
230 char *pszAddVer;
231 char *pszAddVerExt;
232 char *pszAddRev;
233 rc = VbglR3GetAdditionsVersion(&pszAddVer, &pszAddVerExt, &pszAddRev);
234 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Version",
235 "%s", RT_FAILURE(rc) ? "" : pszAddVer);
236 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/VersionExt",
237 "%s", RT_FAILURE(rc) ? "" : pszAddVerExt);
238 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Revision",
239 "%s", RT_FAILURE(rc) ? "" : pszAddRev);
240 if (RT_SUCCESS(rc))
241 {
242 RTStrFree(pszAddVer);
243 RTStrFree(pszAddVerExt);
244 RTStrFree(pszAddRev);
245 }
246
247#ifdef RT_OS_WINDOWS
248 /*
249 * Do windows specific properties.
250 */
251 char *pszInstDir;
252 rc = VbglR3GetAdditionsInstallationPath(&pszInstDir);
253 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/InstallDir",
254 "%s", RT_FAILURE(rc) ? "" : pszInstDir);
255 if (RT_SUCCESS(rc))
256 RTStrFree(pszInstDir);
257
258 VBoxServiceWinGetComponentVersions(g_uVMInfoGuestPropSvcClientID);
259#endif
260}
261
262
263/**
264 * Provide information about active users.
265 */
266static int vboxserviceVMInfoWriteUsers(void)
267{
268 int rc = VINF_SUCCESS;
269 char *pszUserList = NULL;
270 uint32_t cUsersInList = 0;
271
272#ifdef RT_OS_WINDOWS
273# ifndef TARGET_NT4
274 rc = VBoxServiceVMInfoWinWriteUsers(&pszUserList, &cUsersInList);
275# else
276 rc = VERR_NOT_IMPLEMENTED;
277# endif
278
279#elif defined(RT_OS_FREEBSD)
280 /** @todo FreeBSD: Port logged on user info retrieval.
281 * However, FreeBSD 9 supports utmpx, so we could use the code
282 * block below (?). */
283 rc = VERR_NOT_IMPLEMENTED;
284
285#elif defined(RT_OS_OS2)
286 /** @todo OS/2: Port logged on (LAN/local/whatever) user info retrieval. */
287 rc = VERR_NOT_IMPLEMENTED;
288
289#else
290 setutxent();
291 utmpx *ut_user;
292 uint32_t cListSize = 32;
293
294 /* Allocate a first array to hold 32 users max. */
295 char **papszUsers = (char **)RTMemAllocZ(cListSize * sizeof(char *));
296 if (papszUsers == NULL)
297 rc = VERR_NO_MEMORY;
298
299 /* Process all entries in the utmp file. */
300 while ( (ut_user = getutxent())
301 && RT_SUCCESS(rc))
302 {
303 VBoxServiceVerbose(4, "VMInfo/Users: Found logged in user \"%s\"\n",
304 ut_user->ut_user);
305 if (cUsersInList > cListSize)
306 {
307 cListSize += 32;
308 void *pvNew = RTMemRealloc(papszUsers, cListSize * sizeof(char*));
309 AssertPtrBreakStmt(pvNew, cListSize -= 32);
310 papszUsers = (char **)pvNew;
311 }
312
313 /* Make sure we don't add user names which are not
314 * part of type USER_PROCESS. */
315 if (ut_user->ut_type == USER_PROCESS)
316 {
317 bool fFound = false;
318 for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
319 fFound = strcmp(papszUsers[i], ut_user->ut_user) == 0;
320
321 if (!fFound)
322 {
323 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ut_user->ut_user);
324 if (RT_FAILURE(rc))
325 break;
326 cUsersInList++;
327 }
328 }
329 }
330
331 /* Calc the string length. */
332 size_t cchUserList = 0;
333 for (uint32_t i = 0; i < cUsersInList; i++)
334 cchUserList += (i != 0) + strlen(papszUsers[i]);
335
336 /* Build the user list. */
337 rc = RTStrAllocEx(&pszUserList, cchUserList + 1);
338 if (RT_SUCCESS(rc))
339 {
340 char *psz = pszUserList;
341 for (uint32_t i = 0; i < cUsersInList; i++)
342 {
343 if (i != 0)
344 *psz++ = ',';
345 size_t cch = strlen(papszUsers[i]);
346 memcpy(psz, papszUsers[i], cch);
347 psz += cch;
348 }
349 *psz = '\0';
350 }
351
352 /* Cleanup. */
353 for (uint32_t i = 0; i < cUsersInList; i++)
354 RTStrFree(papszUsers[i]);
355 RTMemFree(papszUsers);
356
357 endutxent(); /* Close utmpx file. */
358#endif
359 Assert(RT_FAILURE(rc) || cUsersInList == 0 || (pszUserList && *pszUserList));
360
361 /* If the user enumeration above failed, reset the user count to 0 except
362 * we didn't have enough memory anymore. In that case we want to preserve
363 * the previous user count in order to not confuse third party tools which
364 * rely on that count. */
365 if (RT_FAILURE(rc))
366 {
367 if (rc == VERR_NO_MEMORY)
368 {
369 static int s_iVMInfoBitchedOOM = 0;
370 if (s_iVMInfoBitchedOOM++ < 3)
371 VBoxServiceVerbose(0, "VMInfo/Users: Warning: Not enough memory available to enumerate users! Keeping old value (%u)\n",
372 g_cVMInfoLoggedInUsers);
373 cUsersInList = g_cVMInfoLoggedInUsers;
374 }
375 else
376 cUsersInList = 0;
377 }
378
379 VBoxServiceVerbose(4, "VMInfo/Users: cUsersInList: %u, pszUserList: %s, rc=%Rrc\n",
380 cUsersInList, pszUserList ? pszUserList : "<NULL>", rc);
381
382 if (pszUserList && cUsersInList > 0)
383 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", "%s", pszUserList);
384 else
385 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", NULL);
386 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers", "%u", cUsersInList);
387 if (g_cVMInfoLoggedInUsers != cUsersInList)
388 {
389 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers",
390 cUsersInList == 0 ? "true" : "false");
391 g_cVMInfoLoggedInUsers = cUsersInList;
392 }
393 if (RT_SUCCESS(rc) && pszUserList)
394 RTStrFree(pszUserList);
395 return rc;
396}
397
398
399/**
400 * Provide information about the guest network.
401 */
402static int vboxserviceVMInfoWriteNetwork(void)
403{
404 int rc = VINF_SUCCESS;
405 uint32_t cIfacesReport = 0;
406 char szPropPath[256];
407
408#ifdef RT_OS_WINDOWS
409 IP_ADAPTER_INFO *pAdpInfo = NULL;
410
411# ifndef TARGET_NT4
412 ULONG cbAdpInfo = sizeof(*pAdpInfo);
413 pAdpInfo = (IP_ADAPTER_INFO *)RTMemAlloc(cbAdpInfo);
414 if (!pAdpInfo)
415 {
416 VBoxServiceError("VMInfo/Network: Failed to allocate IP_ADAPTER_INFO\n");
417 return VERR_NO_MEMORY;
418 }
419 DWORD dwRet = GetAdaptersInfo(pAdpInfo, &cbAdpInfo);
420 if (dwRet == ERROR_BUFFER_OVERFLOW)
421 {
422 IP_ADAPTER_INFO *pAdpInfoNew = (IP_ADAPTER_INFO*)RTMemRealloc(pAdpInfo, cbAdpInfo);
423 if (pAdpInfoNew)
424 {
425 pAdpInfo = pAdpInfoNew;
426 dwRet = GetAdaptersInfo(pAdpInfo, &cbAdpInfo);
427 }
428 }
429 else if (dwRet == ERROR_NO_DATA)
430 {
431 VBoxServiceVerbose(3, "VMInfo/Network: No network adapters available\n");
432
433 /* If no network adapters available / present in the
434 * system we pretend success to not bail out too early. */
435 dwRet = ERROR_SUCCESS;
436 }
437
438 if (dwRet != ERROR_SUCCESS)
439 {
440 if (pAdpInfo)
441 RTMemFree(pAdpInfo);
442 VBoxServiceError("VMInfo/Network: Failed to get adapter info: Error %d\n", dwRet);
443 return RTErrConvertFromWin32(dwRet);
444 }
445# endif /* !TARGET_NT4 */
446
447 SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
448 if (sd == SOCKET_ERROR) /* Socket invalid. */
449 {
450 int wsaErr = WSAGetLastError();
451 /* Don't complain/bail out with an error if network stack is not up; can happen
452 * on NT4 due to start up when not connected shares dialogs pop up. */
453 if (WSAENETDOWN == wsaErr)
454 {
455 VBoxServiceVerbose(0, "VMInfo/Network: Network is not up yet.\n");
456 wsaErr = VINF_SUCCESS;
457 }
458 else
459 VBoxServiceError("VMInfo/Network: Failed to get a socket: Error %d\n", wsaErr);
460 if (pAdpInfo)
461 RTMemFree(pAdpInfo);
462 return RTErrConvertFromWin32(wsaErr);
463 }
464
465 INTERFACE_INFO InterfaceList[20] = {0};
466 unsigned long nBytesReturned = 0;
467 if (WSAIoctl(sd,
468 SIO_GET_INTERFACE_LIST,
469 0,
470 0,
471 &InterfaceList,
472 sizeof(InterfaceList),
473 &nBytesReturned,
474 0,
475 0) == SOCKET_ERROR)
476 {
477 VBoxServiceError("VMInfo/Network: Failed to WSAIoctl() on socket: Error: %d\n", WSAGetLastError());
478 if (pAdpInfo)
479 RTMemFree(pAdpInfo);
480 return RTErrConvertFromWin32(WSAGetLastError());
481 }
482 int cIfacesSystem = nBytesReturned / sizeof(INTERFACE_INFO);
483
484 /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
485 for (int i = 0; i < cIfacesSystem; ++i)
486 {
487 sockaddr_in *pAddress;
488 u_long nFlags = 0;
489 if (InterfaceList[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
490 continue;
491 nFlags = InterfaceList[i].iiFlags;
492 pAddress = (sockaddr_in *)&(InterfaceList[i].iiAddress);
493 Assert(pAddress);
494 char szIp[32];
495 RTStrPrintf(szIp, sizeof(szIp), "%s", inet_ntoa(pAddress->sin_addr));
496 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/IP", cIfacesReport);
497 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szIp);
498
499 pAddress = (sockaddr_in *) & (InterfaceList[i].iiBroadcastAddress);
500 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport);
501 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
502
503 pAddress = (sockaddr_in *)&(InterfaceList[i].iiNetmask);
504 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport);
505 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
506
507 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/Status", cIfacesReport);
508 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, nFlags & IFF_UP ? "Up" : "Down");
509
510# ifndef TARGET_NT4
511 IP_ADAPTER_INFO *pAdp;
512 for (pAdp = pAdpInfo; pAdp; pAdp = pAdp->Next)
513 if (!strcmp(pAdp->IpAddressList.IpAddress.String, szIp))
514 break;
515
516 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/MAC", cIfacesReport);
517 if (pAdp)
518 {
519 char szMac[32];
520 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
521 pAdp->Address[0], pAdp->Address[1], pAdp->Address[2],
522 pAdp->Address[3], pAdp->Address[4], pAdp->Address[5]);
523 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
524 }
525 else
526 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, NULL);
527# endif /* !TARGET_NT4 */
528
529 cIfacesReport++;
530 }
531 if (pAdpInfo)
532 RTMemFree(pAdpInfo);
533 if (sd >= 0)
534 closesocket(sd);
535
536#elif defined(RT_OS_FREEBSD)
537 struct ifaddrs *pIfHead = NULL;
538
539 /* Get all available interfaces */
540 rc = getifaddrs(&pIfHead);
541 if (rc < 0)
542 {
543 rc = RTErrConvertFromErrno(errno);
544 VBoxServiceError("VMInfo/Network: Failed to get all interfaces: Error %Rrc\n");
545 return rc;
546 }
547
548 /* Loop through all interfaces and set the data. */
549 for (struct ifaddrs *pIfCurr = pIfHead; pIfCurr; pIfCurr = pIfCurr->ifa_next)
550 {
551 /*
552 * Only AF_INET and no loopback interfaces
553 * @todo: IPv6 interfaces
554 */
555 if ( pIfCurr->ifa_addr->sa_family == AF_INET
556 && !(pIfCurr->ifa_flags & IFF_LOOPBACK))
557 {
558 char szInetAddr[NI_MAXHOST];
559
560 memset(szInetAddr, 0, NI_MAXHOST);
561 getnameinfo(pIfCurr->ifa_addr, sizeof(struct sockaddr_in),
562 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
563 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/IP", cIfacesReport);
564 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
565
566 memset(szInetAddr, 0, NI_MAXHOST);
567 getnameinfo(pIfCurr->ifa_broadaddr, sizeof(struct sockaddr_in),
568 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
569 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport);
570 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
571
572 memset(szInetAddr, 0, NI_MAXHOST);
573 getnameinfo(pIfCurr->ifa_netmask, sizeof(struct sockaddr_in),
574 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
575 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport);
576 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
577
578 /* Search for the AF_LINK interface of the current AF_INET one and get the mac. */
579 for (struct ifaddrs *pIfLinkCurr = pIfHead; pIfLinkCurr; pIfLinkCurr = pIfLinkCurr->ifa_next)
580 {
581 if ( pIfLinkCurr->ifa_addr->sa_family == AF_LINK
582 && !strcmp(pIfCurr->ifa_name, pIfLinkCurr->ifa_name))
583 {
584 char szMac[32];
585 uint8_t *pu8Mac = NULL;
586 struct sockaddr_dl *pLinkAddress = (struct sockaddr_dl *)pIfLinkCurr->ifa_addr;
587
588 AssertPtr(pLinkAddress);
589 pu8Mac = (uint8_t *)LLADDR(pLinkAddress);
590 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
591 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
592 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/MAC", cIfacesReport);
593 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
594 break;
595 }
596 }
597
598 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/Status", cIfacesReport);
599 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down");
600
601 cIfacesReport++;
602 }
603 }
604
605 /* Free allocated resources. */
606 freeifaddrs(pIfHead);
607
608#else /* !RT_OS_WINDOWS && !RT_OS_FREEBSD */
609 int sd = socket(AF_INET, SOCK_DGRAM, 0);
610 if (sd < 0)
611 {
612 rc = RTErrConvertFromErrno(errno);
613 VBoxServiceError("VMInfo/Network: Failed to get a socket: Error %Rrc\n", rc);
614 return rc;
615 }
616
617 ifconf ifcfg;
618 char buffer[1024] = {0};
619 ifcfg.ifc_len = sizeof(buffer);
620 ifcfg.ifc_buf = buffer;
621 if (ioctl(sd, SIOCGIFCONF, &ifcfg) < 0)
622 {
623 close(sd);
624 rc = RTErrConvertFromErrno(errno);
625 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFCONF) on socket: Error %Rrc\n", rc);
626 return rc;
627 }
628
629 ifreq* ifrequest = ifcfg.ifc_req;
630 int cIfacesSystem = ifcfg.ifc_len / sizeof(ifreq);
631
632 for (int i = 0; i < cIfacesSystem; ++i)
633 {
634 sockaddr_in *pAddress;
635 if (ioctl(sd, SIOCGIFFLAGS, &ifrequest[i]) < 0)
636 {
637 rc = RTErrConvertFromErrno(errno);
638 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFFLAGS) on socket: Error %Rrc\n", rc);
639 break;
640 }
641 if (ifrequest[i].ifr_flags & IFF_LOOPBACK) /* Skip the loopback device. */
642 continue;
643
644 bool fIfUp = !!(ifrequest[i].ifr_flags & IFF_UP);
645 pAddress = ((sockaddr_in *)&ifrequest[i].ifr_addr);
646 Assert(pAddress);
647 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/IP", cIfacesReport);
648 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
649
650 if (ioctl(sd, SIOCGIFBRDADDR, &ifrequest[i]) < 0)
651 {
652 rc = RTErrConvertFromErrno(errno);
653 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %Rrc\n", rc);
654 break;
655 }
656 pAddress = (sockaddr_in *)&ifrequest[i].ifr_broadaddr;
657 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport);
658 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
659
660 if (ioctl(sd, SIOCGIFNETMASK, &ifrequest[i]) < 0)
661 {
662 rc = RTErrConvertFromErrno(errno);
663 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFNETMASK) on socket: Error %Rrc\n", rc);
664 break;
665 }
666# if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
667 pAddress = (sockaddr_in *)&ifrequest[i].ifr_addr;
668# else
669 pAddress = (sockaddr_in *)&ifrequest[i].ifr_netmask;
670# endif
671
672 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport);
673 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
674
675# if defined(RT_OS_SOLARIS)
676 /*
677 * "ifreq" is obsolete on Solaris. We use the recommended "lifreq".
678 * We might fail if the interface has not been assigned an IP address.
679 * That doesn't matter; as long as it's plumbed we can pick it up.
680 * But, if it has not acquired an IP address we cannot obtain it's MAC
681 * address this way, so we just use all zeros there.
682 */
683 RTMAC IfMac;
684 RT_ZERO(IfMac);
685 struct lifreq IfReq;
686 RT_ZERO(IfReq);
687 AssertCompile(sizeof(IfReq.lifr_name) >= sizeof(ifrequest[i].ifr_name));
688 strncpy(IfReq.lifr_name, ifrequest[i].ifr_name, sizeof(ifrequest[i].ifr_name));
689 if (ioctl(sd, SIOCGLIFADDR, &IfReq) >= 0)
690 {
691 struct arpreq ArpReq;
692 RT_ZERO(ArpReq);
693 memcpy(&ArpReq.arp_pa, &IfReq.lifr_addr, sizeof(struct sockaddr_in));
694
695 if (ioctl(sd, SIOCGARP, &ArpReq) >= 0)
696 memcpy(&IfMac, ArpReq.arp_ha.sa_data, sizeof(IfMac));
697 else
698 {
699 rc = RTErrConvertFromErrno(errno);
700 VBoxServiceError("VMInfo/Network: failed to ioctl(SIOCGARP) on socket: Error %Rrc\n", rc);
701 break;
702 }
703 }
704 else
705 {
706 VBoxServiceVerbose(2, "VMInfo/Network: Interface %d has no assigned IP address, skipping ...\n", i);
707 continue;
708 }
709# else
710# ifndef RT_OS_OS2 /** @todo port this to OS/2 */
711 if (ioctl(sd, SIOCGIFHWADDR, &ifrequest[i]) < 0)
712 {
713 rc = RTErrConvertFromErrno(errno);
714 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFHWADDR) on socket: Error %Rrc\n", rc);
715 break;
716 }
717# endif
718# endif
719
720# ifndef RT_OS_OS2 /** @todo port this to OS/2 */
721 char szMac[32];
722# if defined(RT_OS_SOLARIS)
723 uint8_t *pu8Mac = IfMac.au8;
724# else
725 uint8_t *pu8Mac = (uint8_t*)&ifrequest[i].ifr_hwaddr.sa_data[0]; /* @todo see above */
726# endif
727 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
728 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
729 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/MAC", cIfacesReport);
730 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
731# endif /* !OS/2*/
732
733 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/Status", cIfacesReport);
734 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, fIfUp ? "Up" : "Down");
735 cIfacesReport++;
736 } /* For all interfaces */
737
738 close(sd);
739 if (RT_FAILURE(rc))
740 VBoxServiceError("VMInfo/Network: Network enumeration for interface %u failed with error %Rrc\n", cIfacesReport, rc);
741
742#endif /* !RT_OS_WINDOWS */
743
744#if 0 /* Zapping not enabled yet, needs more testing first. */
745 /*
746 * Zap all stale network interface data if the former (saved) network ifaces count
747 * is bigger than the current one.
748 */
749
750 /* Get former count. */
751 uint32_t cIfacesReportOld;
752 rc = VBoxServiceReadPropUInt32(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/Net/Count", &cIfacesReportOld,
753 0 /* Min */, UINT32_MAX /* Max */);
754 if ( RT_SUCCESS(rc)
755 && cIfacesReportOld > cIfacesReport) /* Are some ifaces not around anymore? */
756 {
757 VBoxServiceVerbose(3, "VMInfo/Network: Stale interface data detected (%u old vs. %u current)\n",
758 cIfacesReportOld, cIfacesReport);
759
760 uint32_t uIfaceDeleteIdx = cIfacesReport;
761 do
762 {
763 VBoxServiceVerbose(3, "VMInfo/Network: Deleting stale data of interface %d ...\n", uIfaceDeleteIdx);
764 rc = VBoxServicePropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%u", uIfaceDeleteIdx++);
765 } while (RT_SUCCESS(rc));
766 }
767 else if ( RT_FAILURE(rc)
768 && rc != VERR_NOT_FOUND)
769 {
770 VBoxServiceError("VMInfo/Network: Failed retrieving old network interfaces count with error %Rrc\n", rc);
771 }
772#endif
773
774 /*
775 * This property is a beacon which is _always_ written, even if the network configuration
776 * does not change. If this property is missing, the host assumes that all other GuestInfo
777 * properties are no longer valid.
778 */
779 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count", "%d",
780 cIfacesReport);
781
782 /* Don't fail here; just report everything we got. */
783 return VINF_SUCCESS;
784}
785
786
787/** @copydoc VBOXSERVICE::pfnWorker */
788DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown)
789{
790 int rc;
791
792 /*
793 * Tell the control thread that it can continue
794 * spawning services.
795 */
796 RTThreadUserSignal(RTThreadSelf());
797
798#ifdef RT_OS_WINDOWS
799 /* Required for network information (must be called per thread). */
800 WSADATA wsaData;
801 if (WSAStartup(MAKEWORD(2, 2), &wsaData))
802 VBoxServiceError("VMInfo/Network: WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(WSAGetLastError()));
803#endif /* RT_OS_WINDOWS */
804
805 /*
806 * Write the fixed properties first.
807 */
808 vboxserviceVMInfoWriteFixedProperties();
809
810 /*
811 * Now enter the loop retrieving runtime data continuously.
812 */
813 for (;;)
814 {
815 rc = vboxserviceVMInfoWriteUsers();
816 if (RT_FAILURE(rc))
817 break;
818
819 rc = vboxserviceVMInfoWriteNetwork();
820 if (RT_FAILURE(rc))
821 break;
822
823 /*
824 * Flush all properties if we were restored.
825 */
826 uint64_t idNewSession = g_idVMInfoSession;
827 VbglR3GetSessionId(&idNewSession);
828 if (idNewSession != g_idVMInfoSession)
829 {
830 VBoxServiceVerbose(3, "VMInfo: The VM session ID changed, flushing all properties\n");
831 vboxserviceVMInfoWriteFixedProperties();
832 VBoxServicePropCacheFlush(&g_VMInfoPropCache);
833 g_idVMInfoSession = idNewSession;
834 }
835
836 /*
837 * Block for a while.
838 *
839 * The event semaphore takes care of ignoring interruptions and it
840 * allows us to implement service wakeup later.
841 */
842 if (*pfShutdown)
843 break;
844 int rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval);
845 if (*pfShutdown)
846 break;
847 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
848 {
849 VBoxServiceError("VMInfo: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
850 rc = rc2;
851 break;
852 }
853 else if (RT_LIKELY(RT_SUCCESS(rc2)))
854 {
855 /* Reset event semaphore if it got triggered. */
856 rc2 = RTSemEventMultiReset(g_hVMInfoEvent);
857 if (RT_FAILURE(rc2))
858 rc2 = VBoxServiceError("VMInfo: RTSemEventMultiReset failed; rc2=%Rrc\n", rc2);
859 }
860 }
861
862#ifdef RT_OS_WINDOWS
863 WSACleanup();
864#endif
865
866 return rc;
867}
868
869
870/** @copydoc VBOXSERVICE::pfnStop */
871static DECLCALLBACK(void) VBoxServiceVMInfoStop(void)
872{
873 RTSemEventMultiSignal(g_hVMInfoEvent);
874}
875
876
877/** @copydoc VBOXSERVICE::pfnTerm */
878static DECLCALLBACK(void) VBoxServiceVMInfoTerm(void)
879{
880 int rc;
881
882#ifdef RT_OS_WINDOWS
883# ifndef RT_OS_NT4
884 /* Install console control handler. */
885 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)NULL, FALSE /* Remove handler */))
886 {
887 VBoxServiceError("VMInfo: Unable to remove console control handler, error=%ld\n", GetLastError());
888 /* Just skip this error, not critical. */
889 }
890# endif /* !RT_OS_NT4 */
891#endif
892
893 if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
894 {
895 /** @todo temporary solution: Zap all values which are not valid
896 * anymore when VM goes down (reboot/shutdown ). Needs to
897 * be replaced with "temporary properties" later.
898 *
899 * One idea is to introduce a (HGCM-)session guest property
900 * flag meaning that a guest property is only valid as long
901 * as the HGCM session isn't closed (e.g. guest application
902 * terminates). [don't remove till implemented]
903 */
904 /** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and use the cache
905 * since it remembers what we've written. */
906 /* Delete the "../Net" branch. */
907 const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
908 rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
909
910 /* Destroy property cache. */
911 VBoxServicePropCacheDestroy(&g_VMInfoPropCache);
912
913 /* Disconnect from guest properties service. */
914 rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID);
915 if (RT_FAILURE(rc))
916 VBoxServiceError("VMInfo: Failed to disconnect from guest property service! Error: %Rrc\n", rc);
917 g_uVMInfoGuestPropSvcClientID = 0;
918
919 RTSemEventMultiDestroy(g_hVMInfoEvent);
920 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
921 }
922}
923
924
925/**
926 * The 'vminfo' service description.
927 */
928VBOXSERVICE g_VMInfo =
929{
930 /* pszName. */
931 "vminfo",
932 /* pszDescription. */
933 "Virtual Machine Information",
934 /* pszUsage. */
935 " [--vminfo-interval <ms>]"
936 ,
937 /* pszOptions. */
938 " --vminfo-interval Specifies the interval at which to retrieve the\n"
939 " VM information. The default is 10000 ms.\n"
940 ,
941 /* methods */
942 VBoxServiceVMInfoPreInit,
943 VBoxServiceVMInfoOption,
944 VBoxServiceVMInfoInit,
945 VBoxServiceVMInfoWorker,
946 VBoxServiceVMInfoStop,
947 VBoxServiceVMInfoTerm
948};
949
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