VBoxServiceVMInfo.cpp revision 08a41bf1ab08daa44e2875740ccd1ec50c6b8bec
/* $Id$ */
/** @file
* VBoxService - Virtual Machine Information for the Host.
*/
/*
* Copyright (C) 2009-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifdef RT_OS_WINDOWS
# include <winsock2.h>
# include <iphlpapi.h>
# include <ws2tcpip.h>
# include <windows.h>
# include <Ntsecapi.h>
#else
# define __STDC_LIMIT_MACROS
# include <errno.h>
# include <unistd.h>
# ifndef RT_OS_FREEBSD
# include <utmpx.h> /* @todo FreeBSD 9 should have this. */
# endif
# ifdef RT_OS_SOLARIS
# endif
# ifdef RT_OS_FREEBSD
# include <ifaddrs.h> /* getifaddrs, freeifaddrs */
# include <netdb.h> /* getnameinfo */
# endif
#endif
#include <iprt/semaphore.h>
#include <VBox/VBoxGuestLib.h>
#include "VBoxServiceInternal.h"
#include "VBoxServiceUtils.h"
#include "VBoxServicePropCache.h"
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The vminfo interval (millseconds). */
static uint32_t g_cMsVMInfoInterval = 0;
/** The semaphore we're blocking on. */
/** The guest property service client ID. */
static uint32_t g_uVMInfoGuestPropSvcClientID = 0;
/** Number of logged in users in OS. */
/** The guest property cache. */
/** The VM session ID. Changes whenever the VM is restored or reset. */
static uint64_t g_idVMInfoSession;
/** @copydoc VBOXSERVICE::pfnPreInit */
static DECLCALLBACK(int) VBoxServiceVMInfoPreInit(void)
{
return VINF_SUCCESS;
}
/** @copydoc VBOXSERVICE::pfnOption */
static DECLCALLBACK(int) VBoxServiceVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi)
{
int rc = -1;
if (ppszShort)
/* no short options */;
return rc;
}
/** @copydoc VBOXSERVICE::pfnInit */
static DECLCALLBACK(int) VBoxServiceVMInfoInit(void)
{
/*
* If not specified, find the right interval default.
* Then create the event sem to block on.
*/
if (!g_cMsVMInfoInterval)
if (!g_cMsVMInfoInterval)
/* The status code is ignored as this information is not available with VBox < 3.2.10. */
if (RT_SUCCESS(rc))
else
{
/* If the service was not found, we disable this service without
causing VBoxService to fail. */
{
VBoxServiceVerbose(0, "VMInfo: Guest property service is not available, disabling the service\n");
}
else
}
if (RT_SUCCESS(rc))
{
/*
* Declare some guest properties with flags and reset values.
*/
VBOXSERVICEPROPCACHEFLAG_TEMPORARY, "true");
VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */);
}
return rc;
}
/**
* Writes the properties that won't change while the service is running.
*
* Errors are ignored.
*/
static void vboxserviceVMInfoWriteFixedProperties(void)
{
/*
* First get OS information that won't change.
*/
char szInfo[256];
/*
* Retrieve version information about Guest Additions and installed files (components).
*/
char *pszAddVer;
char *pszAddRev;
if (RT_SUCCESS(rc))
{
}
#ifdef RT_OS_WINDOWS
/*
* Do windows specific properties.
*/
char *pszInstDir;
if (RT_SUCCESS(rc))
#endif
}
/**
* Provide information about active users.
*/
static int vboxserviceVMInfoWriteUsers(void)
{
int rc = VINF_SUCCESS;
char *pszUserList = NULL;
uint32_t cUsersInList = 0;
#ifdef RT_OS_WINDOWS
# ifndef TARGET_NT4
# else
# endif
#elif defined(RT_OS_FREEBSD)
/** @todo FreeBSD: Port logged on user info retrival.
* However, FreeBSD 9 supports utmpx, so we could use the code
* block below (?). */
#else
setutxent();
/* Allocate a first array to hold 32 users max. */
if (papszUsers == NULL)
rc = VERR_NO_MEMORY;
/* Process all entries in the utmp file. */
&& RT_SUCCESS(rc))
{
if (cUsersInList > cListSize)
{
cListSize += 32;
papszUsers = (char **)pvNew;
}
/* Make sure we don't add user names which are not
* part of type USER_PROCESS. */
{
bool fFound = false;
if (!fFound)
{
if (RT_FAILURE(rc))
break;
cUsersInList++;
}
}
}
/* Calc the string length. */
size_t cchUserList = 0;
for (uint32_t i = 0; i < cUsersInList; i++)
/* Build the user list. */
if (RT_SUCCESS(rc))
{
char *psz = pszUserList;
for (uint32_t i = 0; i < cUsersInList; i++)
{
if (i != 0)
*psz++ = ',';
}
*psz = '\0';
}
/* Cleanup. */
for (uint32_t i = 0; i < cUsersInList; i++)
RTStrFree(papszUsers[i]);
endutxent(); /* Close utmpx file. */
#endif
if (RT_FAILURE(rc))
cUsersInList = 0;
if (pszUserList && cUsersInList > 0)
VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", "%s", pszUserList);
else
VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers", "%u", cUsersInList);
if (g_cVMInfoLoggedInUsers != cUsersInList)
{
}
return VINF_SUCCESS;
}
/**
* Provide information about the guest network.
*/
static int vboxserviceVMInfoWriteNetwork(void)
{
int rc = VINF_SUCCESS;
uint32_t cIfacesReport = 0;
char szPropPath[256];
#ifdef RT_OS_WINDOWS
# ifndef TARGET_NT4
if (!pAdpInfo)
{
VBoxServiceError("VMInfo/Network: Failed to allocate IP_ADAPTER_INFO\n");
return VERR_NO_MEMORY;
}
if (dwRet == ERROR_BUFFER_OVERFLOW)
{
if (pAdpInfoNew)
{
}
}
if (dwRet != ERROR_SUCCESS)
{
if (pAdpInfo)
return RTErrConvertFromWin32(dwRet);
}
# endif /* !TARGET_NT4 */
{
int wsaErr = WSAGetLastError();
* on NT4 due to start up when not connected shares dialogs pop up. */
if (WSAENETDOWN == wsaErr)
{
VBoxServiceVerbose(0, "VMInfo/Network: Network is not up yet.\n");
}
else
if (pAdpInfo)
return RTErrConvertFromWin32(wsaErr);
}
unsigned long nBytesReturned = 0;
0,
0,
sizeof(InterfaceList),
0,
0) == SOCKET_ERROR)
{
if (pAdpInfo)
return RTErrConvertFromWin32(WSAGetLastError());
}
/** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
for (int i = 0; i < cIfacesSystem; ++i)
{
continue;
char szIp[32];
RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport);
RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport);
# ifndef TARGET_NT4
break;
if (pAdp)
{
char szMac[32];
}
else
# endif /* !TARGET_NT4 */
}
if (pAdpInfo)
if (sd >= 0)
#elif defined(RT_OS_FREEBSD)
/* Get all available interfaces */
if (rc < 0)
{
VBoxServiceError("VMInfo/Network: Failed to get all interfaces: Error %Rrc\n");
return rc;
}
/* Loop through all interfaces and set the data. */
{
/*
* Only AF_INET and no loopback interfaces
* @todo: IPv6 interfaces
*/
{
char szInetAddr[NI_MAXHOST];
RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport);
RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport);
/* Search for the AF_LINK interface of the current AF_INET one and get the mac. */
{
{
char szMac[32];
break;
}
}
VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down");
}
}
/* Free allocated resources. */
#else /* !RT_OS_WINDOWS && !RT_OS_FREEBSD */
if (sd < 0)
{
return rc;
}
char buffer[1024] = {0};
{
return rc;
}
for (int i = 0; i < cIfacesSystem; ++i)
{
{
break;
}
continue;
{
break;
}
RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport);
{
break;
}
# if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
# else
# endif
RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport);
# if defined(RT_OS_SOLARIS)
/*
* "ifreq" is obsolete on Solaris. We use the recommended "lifreq".
* We might fail if the interface has not been assigned an IP address.
* That doesn't matter; as long as it's plumbed we can pick it up.
* But, if it has not acquired an IP address we cannot obtain it's MAC
* address this way, so we just use all zeros there.
*/
{
else
{
break;
}
}
else
{
VBoxServiceVerbose(2, "VMInfo/Network: Interface %d has no assigned IP address, skipping ...\n", i);
continue;
}
# else
{
break;
}
# endif
char szMac[32];
# if defined(RT_OS_SOLARIS)
# else
# endif
}
if (RT_FAILURE(rc))
VBoxServiceError("VMInfo/Network: Network enumeration for interface %u failed with error %Rrc\n", cIfacesReport, rc);
#endif /* !RT_OS_WINDOWS */
#if 0 /* Zapping not enabled yet, needds more testing first. */
/*
* Zap all stale network interface data if the former (saved) network ifaces count
* is bigger than the current one.
*/
/* Get former count. */
rc = VBoxServiceReadPropUInt32(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/Net/Count", &cIfacesReportOld,
if ( RT_SUCCESS(rc)
{
do
{
VBoxServiceVerbose(3, "VMInfo/Network: Deleting stale data of interface %d ...\n", uIfaceDeleteIdx);
rc = VBoxServicePropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%u", uIfaceDeleteIdx++);
} while (RT_SUCCESS(rc));
}
else if ( RT_FAILURE(rc)
&& rc != VERR_NOT_FOUND)
{
VBoxServiceError("VMInfo/Network: Failed retrieving old network interfaces count with error %Rrc\n", rc);
}
#endif
/*
* This property is a beacon which is _always_ written, even if the network configuration
* does not change. If this property is missing, the host assumes that all other GuestInfo
* properties are no longer valid.
*/
/* Don't fail here; just report everything we got. */
return VINF_SUCCESS;
}
/** @copydoc VBOXSERVICE::pfnWorker */
{
int rc;
/*
* Tell the control thread that it can continue
* spawning services.
*/
#ifdef RT_OS_WINDOWS
/* Required for network information (must be called per thread). */
VBoxServiceError("VMInfo/Network: WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(WSAGetLastError()));
#endif /* RT_OS_WINDOWS */
/*
* Write the fixed properties first.
*/
/*
* Now enter the loop retrieving runtime data continuously.
*/
for (;;)
{
if (RT_FAILURE(rc))
break;
if (RT_FAILURE(rc))
break;
/*
* Flush all properties if we were restored.
*/
if (idNewSession != g_idVMInfoSession)
{
}
/*
* Block for a while.
*
* The event semaphore takes care of ignoring interruptions and it
* allows us to implement service wakeup later.
*/
if (*pfShutdown)
break;
if (*pfShutdown)
break;
{
break;
}
}
#ifdef RT_OS_WINDOWS
WSACleanup();
#endif
return rc;
}
/** @copydoc VBOXSERVICE::pfnStop */
static DECLCALLBACK(void) VBoxServiceVMInfoStop(void)
{
}
/** @copydoc VBOXSERVICE::pfnTerm */
static DECLCALLBACK(void) VBoxServiceVMInfoTerm(void)
{
int rc;
if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
{
/** @todo temporary solution: Zap all values which are not valid
* be replaced with "temporary properties" later.
*
* One idea is to introduce a (HGCM-)session guest property
* flag meaning that a guest property is only valid as long
* as the HGCM session isn't closed (e.g. guest application
* terminates). [don't remove till implemented]
*/
/** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and use the cache
* since it remembers what we've written. */
/* Delete the "../Net" branch. */
/* Destroy property cache. */
/* Disconnect from guest properties service. */
if (RT_FAILURE(rc))
}
}
/**
* The 'vminfo' service description.
*/
{
/* pszName. */
"vminfo",
/* pszDescription. */
"Virtual Machine Information",
/* pszUsage. */
" [--vminfo-interval <ms>]"
,
/* pszOptions. */
" --vminfo-interval Specifies the interval at which to retrieve the\n"
" VM information. The default is 10000 ms.\n"
,
/* methods */
};