VirtualBox

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

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

VBoxService/users-win: Added session change detection, more fun with logged-in user detection.

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