VirtualBox

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

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

Argh.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.0 KB
Line 
1/* $Id: VBoxServiceVMInfo.cpp 30875 2010-07-16 11:13:18Z 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_FREEBSD /* The header does not exist anymore since FreeBSD 9-current */
39# include <utmp.h>
40# endif
41# ifdef RT_OS_SOLARIS
42# include <sys/sockio.h>
43# endif
44# ifdef RT_OS_FREEBSD
45# include <ifaddrs.h> /* getifaddrs, freeifaddrs */
46# include <net/if_dl.h> /* LLADDR */
47# include <netdb.h> /* getnameinfo */
48# endif
49#endif
50
51#include <iprt/mem.h>
52#include <iprt/thread.h>
53#include <iprt/string.h>
54#include <iprt/semaphore.h>
55#include <iprt/system.h>
56#include <iprt/time.h>
57#include <iprt/assert.h>
58#include <VBox/version.h>
59#include <VBox/VBoxGuestLib.h>
60#include "VBoxServiceInternal.h"
61#include "VBoxServiceUtils.h"
62#include "VBoxServicePropCache.h"
63
64
65/*******************************************************************************
66* Global Variables *
67*******************************************************************************/
68/** The vminfo interval (millseconds). */
69static uint32_t g_cMsVMInfoInterval = 0;
70/** The semaphore we're blocking on. */
71static RTSEMEVENTMULTI g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
72/** The guest property service client ID. */
73static uint32_t g_uVMInfoGuestPropSvcClientID = 0;
74/** Number of logged in users in OS. */
75static uint32_t g_cVMInfoLoggedInUsers = UINT32_MAX;
76/** The guest property cache. */
77static VBOXSERVICEVEPROPCACHE g_VMInfoPropCache;
78
79
80/** @copydoc VBOXSERVICE::pfnPreInit */
81static DECLCALLBACK(int) VBoxServiceVMInfoPreInit(void)
82{
83 return VINF_SUCCESS;
84}
85
86
87/** @copydoc VBOXSERVICE::pfnOption */
88static DECLCALLBACK(int) VBoxServiceVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi)
89{
90 int rc = -1;
91 if (ppszShort)
92 /* no short options */;
93 else if (!strcmp(argv[*pi], "--vminfo-interval"))
94 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
95 &g_cMsVMInfoInterval, 1, UINT32_MAX - 1);
96 return rc;
97}
98
99
100/** @copydoc VBOXSERVICE::pfnInit */
101static DECLCALLBACK(int) VBoxServiceVMInfoInit(void)
102{
103 /*
104 * If not specified, find the right interval default.
105 * Then create the event sem to block on.
106 */
107 if (!g_cMsVMInfoInterval)
108 g_cMsVMInfoInterval = g_DefaultInterval * 1000;
109 if (!g_cMsVMInfoInterval)
110 g_cMsVMInfoInterval = 10 * 1000;
111
112 int rc = RTSemEventMultiCreate(&g_hVMInfoEvent);
113 AssertRCReturn(rc, rc);
114
115 rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID);
116 if (RT_SUCCESS(rc))
117 VBoxServiceVerbose(3, "VMInfo: Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID);
118 else
119 {
120 /* If the service was not found, we disable this service without
121 causing VBoxService to fail. */
122 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
123 {
124 VBoxServiceVerbose(0, "VMInfo: Guest property service is not available, disabling the service\n");
125 rc = VERR_SERVICE_DISABLED;
126 }
127 else
128 VBoxServiceError("VMInfo: Failed to connect to the guest property service! Error: %Rrc\n", rc);
129 RTSemEventMultiDestroy(g_hVMInfoEvent);
130 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
131 }
132
133 if (RT_SUCCESS(rc))
134 {
135 VBoxServicePropCacheCreate(&g_VMInfoPropCache, g_uVMInfoGuestPropSvcClientID);
136
137 /*
138 * Declare some guest properties with flags and reset values.
139 */
140 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList",
141 VBOXSERVICEPROPCACHEFLAG_TEMPORARY, NULL /* Delete on exit */);
142 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers",
143 VBOXSERVICEPROPCACHEFLAG_TEMPORARY, "0");
144 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers",
145 VBOXSERVICEPROPCACHEFLAG_TEMPORARY, "true");
146 VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count",
147 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */);
148 }
149 return rc;
150}
151
152
153/**
154 * Writes the properties that won't change while the service is running.
155 *
156 * Errors are ignored.
157 */
158static void vboxserviceVMInfoWriteFixedProperties(void)
159{
160 /*
161 * First get OS information that won't change.
162 */
163 char szInfo[256];
164 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szInfo, sizeof(szInfo));
165 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Product",
166 "%s", RT_FAILURE(rc) ? "" : szInfo);
167
168 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szInfo, sizeof(szInfo));
169 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Release",
170 "%s", RT_FAILURE(rc) ? "" : szInfo);
171
172 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szInfo, sizeof(szInfo));
173 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Version",
174 "%s", RT_FAILURE(rc) ? "" : szInfo);
175
176 rc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szInfo, sizeof(szInfo));
177 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/ServicePack",
178 "%s", RT_FAILURE(rc) ? "" : szInfo);
179
180 /*
181 * Retrieve version information about Guest Additions and installed files (components).
182 */
183 char *pszAddVer;
184 char *pszAddRev;
185 rc = VbglR3GetAdditionsVersion(&pszAddVer, &pszAddRev);
186 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Version",
187 "%s", RT_FAILURE(rc) ? "" : pszAddVer);
188 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Revision",
189 "%s", RT_FAILURE(rc) ? "" : pszAddRev);
190 if (RT_SUCCESS(rc))
191 {
192 RTStrFree(pszAddVer);
193 RTStrFree(pszAddRev);
194 }
195
196#ifdef RT_OS_WINDOWS
197 /*
198 * Do windows specific properties.
199 */
200 char *pszInstDir;
201 rc = VbglR3GetAdditionsInstallationPath(&pszInstDir);
202 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/InstallDir",
203 "%s", RT_FAILURE(rc) ? "" : pszInstDir);
204 if (RT_SUCCESS(rc))
205 RTStrFree(pszInstDir);
206
207 VBoxServiceWinGetComponentVersions(g_uVMInfoGuestPropSvcClientID);
208#endif
209}
210
211
212/**
213 * Provide information about active users.
214 */
215static int vboxserviceVMInfoWriteUsers(void)
216{
217 int rc;
218 char *pszUserList = NULL;
219 uint32_t cUsersInList = 0;
220
221#ifdef RT_OS_WINDOWS
222# ifndef TARGET_NT4
223 rc = VBoxServiceVMInfoWinWriteUsers(&pszUserList, &cUsersInList);
224# else
225 rc = VERR_NOT_IMPLEMENTED;
226# endif
227#elif defined(RT_OS_FREEBSD)
228 /** @todo FreeBSD: Port logged on user info retrival. */
229 rc = VERR_NOT_IMPLEMENTED;
230#elif defined(RT_OS_OS2)
231 /** @todo OS/2: Port logged on (LAN/local/whatever) user info retrival. */
232 rc = VERR_NOT_IMPLEMENTED;
233#else
234 /** @todo Add utmpx support? */
235 rc = utmpname(UTMP_FILE);
236# ifdef RT_OS_SOLARIS
237 if (rc != 1)
238# else
239 if (rc != 0)
240# endif
241 {
242 VBoxServiceError("VMInfo/Users: Could not set UTMP file! Error: %ld\n", errno);
243 rc = VERR_GENERAL_FAILURE;
244 }
245 else
246 rc = VINF_SUCCESS;
247
248 setutent();
249 utmp *ut_user;
250 uint32_t cUtmpEntries = 0;
251
252 /* Count all users to allocate a sufficient big array. */
253 while ((ut_user = getutent()))
254 {
255 /* Make sure we don't add user names which are not
256 * part of type USER_PROCESS. */
257 if (ut_user->ut_type == USER_PROCESS)
258 cUtmpEntries++;
259 }
260
261 if (cUtmpEntries) /* Do we have some users at all? */
262 {
263 setutent(); /* Rewind utmp file pointer to start from beginning. */
264
265 /* Build up array with logged in users. */
266 char **aUsers = (char**)RTMemAlloc(cUtmpEntries * sizeof(char*));
267 if (aUsers)
268 {
269 /* Store user in array. */
270 while ( (ut_user = getutent())
271 && RT_SUCCESS(rc))
272 {
273 if (ut_user->ut_type == USER_PROCESS)
274 {
275 bool fFound = false;
276 for (uint32_t u = 0; u < cUsersInList && !fFound; u++)
277 fFound = (strcmp((const char*)aUsers[u], ut_user->ut_user) == 0) ? true : false;
278
279 if (!fFound)
280 rc = RTStrAAppend(&aUsers[cUsersInList++], (const char*)ut_user->ut_user);
281 }
282 }
283
284 /* Build final user list. */
285 for (uint32_t u = 0; u < cUsersInList; u++)
286 {
287 if (u > 0)
288 {
289 rc = RTStrAAppend(&pszUserList, ",");
290 AssertRCBreakStmt(rc, RTStrFree(pszUserList));
291 }
292 rc = RTStrAAppend(&pszUserList, (const char*)aUsers[u]);
293 AssertRCBreakStmt(rc, RTStrFree(pszUserList));
294 }
295
296 /* Cleanup. */
297 for (uint32_t u = 0; u < cUsersInList; u++)
298 RTStrFree(aUsers[u]);
299 RTMemFree(aUsers);
300 }
301 else
302 rc = VERR_NO_MEMORY;
303 }
304 endutent(); /* Close utmp file. */
305#endif
306 Assert(RT_FAILURE(rc) || cUsersInList == 0 || (pszUserList && *pszUserList));
307 if (RT_FAILURE(rc))
308 cUsersInList = 0;
309
310 if (pszUserList && cUsersInList > 0)
311 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", "%s", pszUserList);
312 else
313 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", NULL);
314 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers", "%u", cUsersInList);
315 if (g_cVMInfoLoggedInUsers != cUsersInList)
316 {
317 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers",
318 cUsersInList == 0 ? "true" : "false");
319 g_cVMInfoLoggedInUsers = cUsersInList;
320 }
321 if (RT_SUCCESS(rc) && pszUserList)
322 RTStrFree(pszUserList);
323 return VINF_SUCCESS;
324}
325
326
327/**
328 * Provide information about the guest network.
329 */
330static int vboxserviceVMInfoWriteNetwork(void)
331{
332 int cIfacesReport = 0;
333 char szPropPath[256];
334
335#ifdef RT_OS_WINDOWS
336 IP_ADAPTER_INFO *pAdpInfo = NULL;
337
338# ifndef TARGET_NT4
339 ULONG cbAdpInfo = sizeof(*pAdpInfo);
340 pAdpInfo = (IP_ADAPTER_INFO *)RTMemAlloc(cbAdpInfo);
341 if (!pAdpInfo)
342 {
343 VBoxServiceError("VMInfo/Network: Failed to allocate IP_ADAPTER_INFO\n");
344 return VERR_NO_MEMORY;
345 }
346 DWORD dwRet = GetAdaptersInfo(pAdpInfo, &cbAdpInfo);
347 if (dwRet == ERROR_BUFFER_OVERFLOW)
348 {
349 IP_ADAPTER_INFO *pAdpInfoNew = (IP_ADAPTER_INFO*)RTMemRealloc(pAdpInfo, cbAdpInfo);
350 if (pAdpInfoNew)
351 {
352 pAdpInfo = pAdpInfoNew;
353 dwRet = GetAdaptersInfo(pAdpInfo, &cbAdpInfo);
354 }
355 }
356 if (dwRet != ERROR_SUCCESS)
357 {
358 if (pAdpInfo)
359 RTMemFree(pAdpInfo);
360 VBoxServiceError("VMInfo/Network: Failed to get adapter info: Error %d\n", dwRet);
361 return RTErrConvertFromWin32(dwRet);
362 }
363# endif
364
365 SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
366 if (sd == SOCKET_ERROR) /* Socket invalid. */
367 {
368 VBoxServiceError("VMInfo/Network: Failed to get a socket: Error %d\n", WSAGetLastError());
369 if (pAdpInfo)
370 RTMemFree(pAdpInfo);
371 return RTErrConvertFromWin32(WSAGetLastError());
372 }
373
374 INTERFACE_INFO InterfaceList[20] = {0};
375 unsigned long nBytesReturned = 0;
376 if (WSAIoctl(sd,
377 SIO_GET_INTERFACE_LIST,
378 0,
379 0,
380 &InterfaceList,
381 sizeof(InterfaceList),
382 &nBytesReturned,
383 0,
384 0) == SOCKET_ERROR)
385 {
386 VBoxServiceError("VMInfo/Network: Failed to WSAIoctl() on socket: Error: %d\n", WSAGetLastError());
387 if (pAdpInfo)
388 RTMemFree(pAdpInfo);
389 return RTErrConvertFromWin32(WSAGetLastError());
390 }
391 int cIfacesSystem = nBytesReturned / sizeof(INTERFACE_INFO);
392
393 /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
394 for (int i = 0; i < cIfacesSystem; ++i)
395 {
396 sockaddr_in *pAddress;
397 u_long nFlags = 0;
398 if (InterfaceList[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
399 continue;
400 nFlags = InterfaceList[i].iiFlags;
401 pAddress = (sockaddr_in *)&(InterfaceList[i].iiAddress);
402 Assert(pAddress);
403 char szIp[32];
404 RTStrPrintf(szIp, sizeof(szIp), "%s", inet_ntoa(pAddress->sin_addr));
405 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/IP", cIfacesReport);
406 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szIp);
407
408 pAddress = (sockaddr_in *) & (InterfaceList[i].iiBroadcastAddress);
409 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/Broadcast", cIfacesReport);
410 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
411
412 pAddress = (sockaddr_in *)&(InterfaceList[i].iiNetmask);
413 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/Netmask", cIfacesReport);
414 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
415
416 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/Status", cIfacesReport);
417 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, nFlags & IFF_UP ? "Up" : "Down");
418
419# ifndef TARGET_NT4
420 IP_ADAPTER_INFO *pAdp;
421 for (pAdp = pAdpInfo; pAdp; pAdp = pAdp->Next)
422 if (!strcmp(pAdp->IpAddressList.IpAddress.String, szIp))
423 break;
424
425 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/MAC", cIfacesReport);
426 if (pAdp)
427 {
428 char szMac[32];
429 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
430 pAdp->Address[0], pAdp->Address[1], pAdp->Address[2],
431 pAdp->Address[3], pAdp->Address[4], pAdp->Address[5]);
432 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
433 }
434 else
435 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, NULL);
436# endif
437
438 cIfacesReport++;
439 }
440 if (pAdpInfo)
441 RTMemFree(pAdpInfo);
442 if (sd >= 0)
443 closesocket(sd);
444
445#elif defined(RT_OS_FREEBSD)
446 int rc = 0;
447 struct ifaddrs *pIfHead = NULL;
448
449 /* Get all available interfaces */
450 rc = getifaddrs(&pIfHead);
451 if (rc < 0)
452 {
453 VBoxServiceError("VMInfo/Network: Failed to get all interfaces: Error %d\n", errno);
454 return RTErrConvertFromErrno(errno);
455 }
456
457 /* Loop through all interfaces and set the data. */
458 for (struct ifaddrs *pIfCurr = pIfHead; pIfCurr; pIfCurr = pIfCurr->ifa_next)
459 {
460 /*
461 * Only AF_INET and no loopback interfaces
462 * @todo: IPv6 interfaces
463 */
464 if ( pIfCurr->ifa_addr->sa_family == AF_INET
465 && !(pIfCurr->ifa_flags & IFF_LOOPBACK))
466 {
467 char szInetAddr[NI_MAXHOST];
468
469 memset(szInetAddr, 0, NI_MAXHOST);
470 getnameinfo(pIfCurr->ifa_addr, sizeof(struct sockaddr_in),
471 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
472 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/IP", cIfacesReport);
473 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
474
475 memset(szInetAddr, 0, NI_MAXHOST);
476 getnameinfo(pIfCurr->ifa_broadaddr, sizeof(struct sockaddr_in),
477 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
478 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/Broadcast", cIfacesReport);
479 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
480
481 memset(szInetAddr, 0, NI_MAXHOST);
482 getnameinfo(pIfCurr->ifa_netmask, sizeof(struct sockaddr_in),
483 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
484 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/Netmask", cIfacesReport);
485 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
486
487 /* Search for the AF_LINK interface of the current AF_INET one and get the mac. */
488 for (struct ifaddrs *pIfLinkCurr = pIfHead; pIfLinkCurr; pIfLinkCurr = pIfLinkCurr->ifa_next)
489 {
490 if ( pIfLinkCurr->ifa_addr->sa_family == AF_LINK
491 && !strcmp(pIfCurr->ifa_name, pIfLinkCurr->ifa_name))
492 {
493 char szMac[32];
494 uint8_t *pu8Mac = NULL;
495 struct sockaddr_dl *pLinkAddress = (struct sockaddr_dl *)pIfLinkCurr->ifa_addr;
496
497 AssertPtr(pLinkAddress);
498 pu8Mac = (uint8_t *)LLADDR(pLinkAddress);
499 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
500 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
501 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/MAC", cIfacesReport);
502 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
503 break;
504 }
505 }
506
507 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/Status", cIfacesReport);
508 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down");
509
510 cIfacesReport++;
511 }
512 }
513
514 /* Free allocated resources. */
515 freeifaddrs(pIfHead);
516
517#else /* !RT_OS_WINDOWS && !RT_OS_FREEBSD */
518 int sd = socket(AF_INET, SOCK_DGRAM, 0);
519 if (sd < 0)
520 {
521 VBoxServiceError("VMInfo/Network: Failed to get a socket: Error %d\n", errno);
522 return RTErrConvertFromErrno(errno);
523 }
524
525 ifconf ifcfg;
526 char buffer[1024] = {0};
527 ifcfg.ifc_len = sizeof(buffer);
528 ifcfg.ifc_buf = buffer;
529 if (ioctl(sd, SIOCGIFCONF, &ifcfg) < 0)
530 {
531 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFCONF) on socket: Error %d\n", errno);
532 return RTErrConvertFromErrno(errno);
533 }
534
535 ifreq* ifrequest = ifcfg.ifc_req;
536 int cIfacesSystem = ifcfg.ifc_len / sizeof(ifreq);
537
538 for (int i = 0; i < cIfacesSystem; ++i)
539 {
540 sockaddr_in *pAddress;
541 if (ioctl(sd, SIOCGIFFLAGS, &ifrequest[i]) < 0)
542 {
543 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFFLAGS) on socket: Error %d\n", errno);
544 close(sd);
545 return RTErrConvertFromErrno(errno);
546 }
547 if (ifrequest[i].ifr_flags & IFF_LOOPBACK) /* Skip the loopback device. */
548 continue;
549
550 bool fIfUp = !!(ifrequest[i].ifr_flags & IFF_UP);
551 pAddress = ((sockaddr_in *)&ifrequest[i].ifr_addr);
552 Assert(pAddress);
553 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/IP", cIfacesReport);
554 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
555
556 if (ioctl(sd, SIOCGIFBRDADDR, &ifrequest[i]) < 0)
557 {
558 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %d\n", errno);
559 close(sd);
560 return RTErrConvertFromErrno(errno);
561 }
562 pAddress = (sockaddr_in *)&ifrequest[i].ifr_broadaddr;
563 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/Broadcast", cIfacesReport);
564 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
565
566 if (ioctl(sd, SIOCGIFNETMASK, &ifrequest[i]) < 0)
567 {
568 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %d\n", errno);
569 close(sd);
570 return RTErrConvertFromErrno(errno);
571 }
572# if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
573 pAddress = (sockaddr_in *)&ifrequest[i].ifr_addr;
574# else
575 pAddress = (sockaddr_in *)&ifrequest[i].ifr_netmask;
576# endif
577
578 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/V4/Netmask", cIfacesReport);
579 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
580
581# if defined(RT_OS_SOLARIS)
582 if (ioctl(sd, SIOCGENADDR, &ifrequest[i]) < 0)
583# else
584 if (ioctl(sd, SIOCGIFHWADDR, &ifrequest[i]) < 0)
585# endif
586 {
587 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFHWADDR) on socket: Error %d\n", errno);
588 close(sd);
589 return RTErrConvertFromErrno(errno);
590 }
591
592 char szMac[32];
593# if defined(RT_OS_SOLARIS)
594 uint8_t *pu8Mac = (uint8_t*)&ifrequest[i].ifr_enaddr[0];
595# else
596 uint8_t *pu8Mac = (uint8_t*)&ifrequest[i].ifr_hwaddr.sa_data[0];
597# endif
598 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
599 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
600 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/MAC", cIfacesReport);
601 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
602
603 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%d/Status", cIfacesReport);
604 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, fIfUp ? "Up" : "Down");
605 cIfacesReport++;
606 }
607
608 close(sd);
609
610#endif /* !RT_OS_WINDOWS */
611
612 /*
613 * This property is a beacon which is _always_ written, even if the network configuration
614 * does not change. If this property is missing, the host assumes that all other GuestInfo
615 * properties are no longer valid.
616 */
617 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count", "%d",
618 cIfacesReport);
619
620 /** @todo r=bird: if cIfacesReport decreased compared to the previous run, zap
621 * the stale data. This can probably be encorporated into the cache. */
622
623 return VINF_SUCCESS;
624}
625
626
627/** @copydoc VBOXSERVICE::pfnWorker */
628DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown)
629{
630 int rc;
631
632 /*
633 * Tell the control thread that it can continue
634 * spawning services.
635 */
636 RTThreadUserSignal(RTThreadSelf());
637
638#ifdef RT_OS_WINDOWS
639 /* Required for network information (must be called per thread). */
640 WSADATA wsaData;
641 if (WSAStartup(MAKEWORD(2, 2), &wsaData))
642 VBoxServiceError("VMInfo/Network: WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(WSAGetLastError()));
643#endif /* RT_OS_WINDOWS */
644
645 /*
646 * Write the fixed properties first.
647 */
648 vboxserviceVMInfoWriteFixedProperties();
649
650 /*
651 * Now enter the loop retrieving runtime data continuously.
652 */
653 for (;;)
654 {
655 rc = vboxserviceVMInfoWriteUsers();
656 if (RT_FAILURE(rc))
657 break;
658
659 rc = vboxserviceVMInfoWriteNetwork();
660 if (RT_FAILURE(rc))
661 break;
662
663 /*
664 * Block for a while.
665 *
666 * The event semaphore takes care of ignoring interruptions and it
667 * allows us to implement service wakeup later.
668 */
669 if (*pfShutdown)
670 break;
671 int rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval);
672 if (*pfShutdown)
673 break;
674 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
675 {
676 VBoxServiceError("VMInfo: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
677 rc = rc2;
678 break;
679 }
680 }
681
682#ifdef RT_OS_WINDOWS
683 WSACleanup();
684#endif
685
686 return rc;
687}
688
689
690/** @copydoc VBOXSERVICE::pfnStop */
691static DECLCALLBACK(void) VBoxServiceVMInfoStop(void)
692{
693 RTSemEventMultiSignal(g_hVMInfoEvent);
694}
695
696
697/** @copydoc VBOXSERVICE::pfnTerm */
698static DECLCALLBACK(void) VBoxServiceVMInfoTerm(void)
699{
700 int rc;
701
702 if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
703 {
704 /** @todo temporary solution: Zap all values which are not valid
705 * anymore when VM goes down (reboot/shutdown ). Needs to
706 * be replaced with "temporary properties" later.
707 *
708 * One idea is to introduce a (HGCM-)session guest property
709 * flag meaning that a guest property is only valid as long
710 * as the HGCM session isn't closed (e.g. guest application
711 * terminates). [don't remove till implemented]
712 */
713 /** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and use the cache
714 * since it remembers what we've written. */
715 /* Delete the "../Net" branch. */
716 const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
717 rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
718
719 /* Destroy property cache. */
720 VBoxServicePropCacheDestroy(&g_VMInfoPropCache);
721
722 /* Disconnect from guest properties service. */
723 rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID);
724 if (RT_FAILURE(rc))
725 VBoxServiceError("VMInfo: Failed to disconnect from guest property service! Error: %Rrc\n", rc);
726 g_uVMInfoGuestPropSvcClientID = 0;
727
728 RTSemEventMultiDestroy(g_hVMInfoEvent);
729 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
730 }
731}
732
733
734/**
735 * The 'vminfo' service description.
736 */
737VBOXSERVICE g_VMInfo =
738{
739 /* pszName. */
740 "vminfo",
741 /* pszDescription. */
742 "Virtual Machine Information",
743 /* pszUsage. */
744 " [--vminfo-interval <ms>]"
745 ,
746 /* pszOptions. */
747 " --vminfo-interval Specifies the interval at which to retrieve the\n"
748 " VM information. The default is 10000 ms.\n"
749 ,
750 /* methods */
751 VBoxServiceVMInfoPreInit,
752 VBoxServiceVMInfoOption,
753 VBoxServiceVMInfoInit,
754 VBoxServiceVMInfoWorker,
755 VBoxServiceVMInfoStop,
756 VBoxServiceVMInfoTerm
757};
758
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette