VirtualBox

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

Last change on this file since 58033 was 58033, checked in by vboxsync, 9 years ago

VBoxService: Started adding pages with some details for each component.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.6 KB
Line 
1/* $Id: VBoxServiceVMInfo.cpp 58033 2015-10-05 22:12:28Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host.
4 */
5
6/*
7 * Copyright (C) 2009-2015 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/** @page pg_vgsvc_vminfo VBoxService - VM Information
19 *
20 * The VM Information subservice provides heaps of useful information about the
21 * VM via guest properties. Check out the "/VirtualBox/GuestInfo/" part of the
22 * guest properties.
23 *
24 *
25 * @section sec_vgsvc_vminfo_beacons Beacons
26 *
27 * The subservice does not write properties unless there are changes. So, in
28 * order for the host side to know that information is up to date despite an
29 * oldish timestamp we define a couple of values that are always updated and can
30 * reliably used to figure how old the information actually is.
31 *
32 * For the networking part "/VirtualBox/GuestInfo/Net/Count" is the value to
33 * watch out for.
34 *
35 * For the login part, it's possible that we intended to use
36 * "/VirtualBox/GuestInfo/OS/LoggedInUsers" for this, however it is not defined
37 * correctly and current does NOT work as a beacon.
38 *
39 */
40
41
42/*********************************************************************************************************************************
43* Header Files *
44*********************************************************************************************************************************/
45#ifdef RT_OS_WINDOWS
46# ifdef TARGET_NT4 /* HACK ALERT! PMIB_IPSTATS undefined if 0x0400 with newer SDKs. */
47# undef _WIN32_WINNT
48# define _WIN32_WINNT 0x0500
49# endif
50# include <winsock2.h>
51# include <iphlpapi.h>
52# include <ws2tcpip.h>
53# include <windows.h>
54# include <Ntsecapi.h>
55#else
56# define __STDC_LIMIT_MACROS
57# include <arpa/inet.h>
58# include <errno.h>
59# include <netinet/in.h>
60# include <sys/ioctl.h>
61# include <sys/socket.h>
62# include <net/if.h>
63# include <pwd.h> /* getpwuid */
64# include <unistd.h>
65# if !defined(RT_OS_OS2) && !defined(RT_OS_FREEBSD) && !defined(RT_OS_HAIKU)
66# include <utmpx.h> /* @todo FreeBSD 9 should have this. */
67# endif
68# ifdef RT_OS_OS2
69# include <net/if_dl.h>
70# endif
71# ifdef RT_OS_SOLARIS
72# include <sys/sockio.h>
73# include <net/if_arp.h>
74# endif
75# if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
76# include <ifaddrs.h> /* getifaddrs, freeifaddrs */
77# include <net/if_dl.h> /* LLADDR */
78# include <netdb.h> /* getnameinfo */
79# endif
80# ifdef VBOX_WITH_DBUS
81# include <VBox/dbus.h>
82# endif
83#endif
84
85#include <iprt/mem.h>
86#include <iprt/thread.h>
87#include <iprt/string.h>
88#include <iprt/semaphore.h>
89#include <iprt/system.h>
90#include <iprt/time.h>
91#include <iprt/assert.h>
92#include <VBox/version.h>
93#include <VBox/VBoxGuestLib.h>
94#include "VBoxServiceInternal.h"
95#include "VBoxServiceUtils.h"
96#include "VBoxServicePropCache.h"
97
98
99/** Structure containing information about a location awarness
100 * client provided by the host. */
101/** @todo Move this (and functions) into VbglR3. */
102typedef struct VBOXSERVICELACLIENTINFO
103{
104 uint32_t uID;
105 char *pszName;
106 char *pszLocation;
107 char *pszDomain;
108 bool fAttached;
109 uint64_t uAttachedTS;
110} VBOXSERVICELACLIENTINFO, *PVBOXSERVICELACLIENTINFO;
111
112
113/*********************************************************************************************************************************
114* Global Variables *
115*********************************************************************************************************************************/
116/** The vminfo interval (milliseconds). */
117static uint32_t g_cMsVMInfoInterval = 0;
118/** The semaphore we're blocking on. */
119static RTSEMEVENTMULTI g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
120/** The guest property service client ID. */
121static uint32_t g_uVMInfoGuestPropSvcClientID = 0;
122/** Number of currently logged in users in OS. */
123static uint32_t g_cVMInfoLoggedInUsers = 0;
124/** The guest property cache. */
125static VBOXSERVICEVEPROPCACHE g_VMInfoPropCache;
126static const char *g_pszPropCacheValLoggedInUsersList = "/VirtualBox/GuestInfo/OS/LoggedInUsersList";
127static const char *g_pszPropCacheValLoggedInUsers = "/VirtualBox/GuestInfo/OS/LoggedInUsers";
128static const char *g_pszPropCacheValNoLoggedInUsers = "/VirtualBox/GuestInfo/OS/NoLoggedInUsers";
129static const char *g_pszPropCacheValNetCount = "/VirtualBox/GuestInfo/Net/Count";
130/** A guest user's guest property root key. */
131static const char *g_pszPropCacheValUser = "/VirtualBox/GuestInfo/User/";
132/** The VM session ID. Changes whenever the VM is restored or reset. */
133static uint64_t g_idVMInfoSession;
134/** The last attached locartion awareness (LA) client timestamp. */
135static uint64_t g_LAClientAttachedTS = 0;
136/** The current LA client info. */
137static VBOXSERVICELACLIENTINFO g_LAClientInfo;
138/** User idle threshold (in ms). This specifies the minimum time a user is considered
139 * as being idle and then will be reported to the host. Default is 5s. */
140uint32_t g_uVMInfoUserIdleThresholdMS = 5 * 1000;
141
142
143/*********************************************************************************************************************************
144* Defines *
145*********************************************************************************************************************************/
146static const char *g_pszLAActiveClient = "/VirtualBox/HostInfo/VRDP/ActiveClient";
147
148#ifdef VBOX_WITH_DBUS
149/** @name ConsoleKit defines (taken from 0.4.5).
150 * @{ */
151# define CK_NAME "org.freedesktop.ConsoleKit"
152# define CK_PATH "/org/freedesktop/ConsoleKit"
153# define CK_INTERFACE "org.freedesktop.ConsoleKit"
154# define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
155# define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
156# define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat"
157# define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
158/** @} */
159#endif
160
161
162
163/**
164 * Signals the event so that a re-enumeration of VM-specific
165 * information (like logged in users) can happen.
166 *
167 * @return IPRT status code.
168 */
169int VGSvcVMInfoSignal(void)
170{
171 /* Trigger a re-enumeration of all logged-in users by unblocking
172 * the multi event semaphore of the VMInfo thread. */
173 if (g_hVMInfoEvent)
174 return RTSemEventMultiSignal(g_hVMInfoEvent);
175
176 return VINF_SUCCESS;
177}
178
179
180/**
181 * @interface_method_impl{VBOXSERVICE,pfnPreInit}
182 */
183static DECLCALLBACK(int) vbsvcVMInfoPreInit(void)
184{
185 return VINF_SUCCESS;
186}
187
188
189/**
190 * @interface_method_impl{VBOXSERVICE,pfnOption}
191 */
192static DECLCALLBACK(int) vbsvcVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi)
193{
194 /** @todo Use RTGetOpt here. */
195
196 int rc = -1;
197 if (ppszShort)
198 /* no short options */;
199 else if (!strcmp(argv[*pi], "--vminfo-interval"))
200 rc = VGSvcArgUInt32(argc, argv, "", pi, &g_cMsVMInfoInterval, 1, UINT32_MAX - 1);
201 else if (!strcmp(argv[*pi], "--vminfo-user-idle-threshold"))
202 rc = VGSvcArgUInt32(argc, argv, "", pi, &g_uVMInfoUserIdleThresholdMS, 1, UINT32_MAX - 1);
203 return rc;
204}
205
206
207/**
208 * @interface_method_impl{VBOXSERVICE,pfnInit}
209 */
210static DECLCALLBACK(int) vbsvcVMInfoInit(void)
211{
212 /*
213 * If not specified, find the right interval default.
214 * Then create the event sem to block on.
215 */
216 if (!g_cMsVMInfoInterval)
217 g_cMsVMInfoInterval = g_DefaultInterval * 1000;
218 if (!g_cMsVMInfoInterval)
219 {
220 /* Set it to 5s by default for location awareness checks. */
221 g_cMsVMInfoInterval = 5 * 1000;
222 }
223
224 int rc = RTSemEventMultiCreate(&g_hVMInfoEvent);
225 AssertRCReturn(rc, rc);
226
227 VbglR3GetSessionId(&g_idVMInfoSession);
228 /* The status code is ignored as this information is not available with VBox < 3.2.10. */
229
230 /* Initialize the LA client object. */
231 RT_ZERO(g_LAClientInfo);
232
233 rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID);
234 if (RT_SUCCESS(rc))
235 VGSvcVerbose(3, "Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID);
236 else
237 {
238 /* If the service was not found, we disable this service without
239 causing VBoxService to fail. */
240 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
241 {
242 VGSvcVerbose(0, "Guest property service is not available, disabling the service\n");
243 rc = VERR_SERVICE_DISABLED;
244 }
245 else
246 VGSvcError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
247 RTSemEventMultiDestroy(g_hVMInfoEvent);
248 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
249 }
250
251 if (RT_SUCCESS(rc))
252 {
253 VGSvcPropCacheCreate(&g_VMInfoPropCache, g_uVMInfoGuestPropSvcClientID);
254
255 /*
256 * Declare some guest properties with flags and reset values.
257 */
258 int rc2 = VGSvcPropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList,
259 VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT,
260 NULL /* Delete on exit */);
261 if (RT_FAILURE(rc2))
262 VGSvcError("Failed to init property cache value '%s', rc=%Rrc\n", g_pszPropCacheValLoggedInUsersList, rc2);
263
264 rc2 = VGSvcPropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers,
265 VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT, "0");
266 if (RT_FAILURE(rc2))
267 VGSvcError("Failed to init property cache value '%s', rc=%Rrc\n", g_pszPropCacheValLoggedInUsers, rc2);
268
269 rc2 = VGSvcPropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers,
270 VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT, "true");
271 if (RT_FAILURE(rc2))
272 VGSvcError("Failed to init property cache value '%s', rc=%Rrc\n", g_pszPropCacheValNoLoggedInUsers, rc2);
273
274 rc2 = VGSvcPropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValNetCount,
275 VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE,
276 NULL /* Delete on exit */);
277 if (RT_FAILURE(rc2))
278 VGSvcError("Failed to init property cache value '%s', rc=%Rrc\n", g_pszPropCacheValNetCount, rc2);
279
280 /*
281 * Get configuration guest properties from the host.
282 * Note: All properties should have sensible defaults in case the lookup here fails.
283 */
284 char *pszValue;
285 rc2 = VGSvcReadHostProp(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--vminfo-user-idle-threshold",
286 true /* Read only */, &pszValue, NULL /* Flags */, NULL /* Timestamp */);
287 if (RT_SUCCESS(rc2))
288 {
289 AssertPtr(pszValue);
290 g_uVMInfoUserIdleThresholdMS = RT_CLAMP(RTStrToUInt32(pszValue), 1000, UINT32_MAX - 1);
291 RTStrFree(pszValue);
292 }
293 }
294 return rc;
295}
296
297
298/**
299 * Retrieves a specifiy client LA property.
300 *
301 * @return IPRT status code.
302 * @param uClientID LA client ID to retrieve property for.
303 * @param pszProperty Property (without path) to retrieve.
304 * @param ppszValue Where to store value of property.
305 * @param puTimestamp Timestamp of property to retrieve. Optional.
306 */
307static int vgsvcGetLAClientValue(uint32_t uClientID, const char *pszProperty, char **ppszValue, uint64_t *puTimestamp)
308{
309 AssertReturn(uClientID, VERR_INVALID_PARAMETER);
310 AssertPtrReturn(pszProperty, VERR_INVALID_POINTER);
311
312 int rc;
313
314 char pszClientPath[255];
315/** @todo r=bird: Another pointless RTStrPrintf test with wrong status code to boot. */
316 if (RTStrPrintf(pszClientPath, sizeof(pszClientPath),
317 "/VirtualBox/HostInfo/VRDP/Client/%RU32/%s", uClientID, pszProperty))
318 {
319 rc = VGSvcReadHostProp(g_uVMInfoGuestPropSvcClientID, pszClientPath, true /* Read only */,
320 ppszValue, NULL /* Flags */, puTimestamp);
321 }
322 else
323 rc = VERR_NO_MEMORY;
324
325 return rc;
326}
327
328
329/**
330 * Retrieves LA client information. On success the returned structure will have allocated
331 * objects which need to be free'd with vboxServiceFreeLAClientInfo.
332 *
333 * @return IPRT status code.
334 * @param uClientID Client ID to retrieve information for.
335 * @param pClient Pointer where to store the client information.
336 */
337static int vgsvcGetLAClientInfo(uint32_t uClientID, PVBOXSERVICELACLIENTINFO pClient)
338{
339 AssertReturn(uClientID, VERR_INVALID_PARAMETER);
340 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
341
342 int rc = vgsvcGetLAClientValue(uClientID, "Name", &pClient->pszName,
343 NULL /* Timestamp */);
344 if (RT_SUCCESS(rc))
345 {
346 char *pszAttach;
347 rc = vgsvcGetLAClientValue(uClientID, "Attach", &pszAttach, &pClient->uAttachedTS);
348 if (RT_SUCCESS(rc))
349 {
350 AssertPtr(pszAttach);
351 pClient->fAttached = RTStrICmp(pszAttach, "1") == 0;
352
353 RTStrFree(pszAttach);
354 }
355 }
356 if (RT_SUCCESS(rc))
357 rc = vgsvcGetLAClientValue(uClientID, "Location", &pClient->pszLocation, NULL /* Timestamp */);
358 if (RT_SUCCESS(rc))
359 rc = vgsvcGetLAClientValue(uClientID, "Domain", &pClient->pszDomain, NULL /* Timestamp */);
360 if (RT_SUCCESS(rc))
361 pClient->uID = uClientID;
362
363 return rc;
364}
365
366
367/**
368 * Frees all allocated LA client information of a structure.
369 *
370 * @param pClient Pointer to client information structure to free.
371 */
372static void vgsvcFreeLAClientInfo(PVBOXSERVICELACLIENTINFO pClient)
373{
374 if (pClient)
375 {
376 if (pClient->pszName)
377 {
378 RTStrFree(pClient->pszName);
379 pClient->pszName = NULL;
380 }
381 if (pClient->pszLocation)
382 {
383 RTStrFree(pClient->pszLocation);
384 pClient->pszLocation = NULL;
385 }
386 if (pClient->pszDomain)
387 {
388 RTStrFree(pClient->pszDomain);
389 pClient->pszDomain = NULL;
390 }
391 }
392}
393
394
395/**
396 * Updates a per-guest user guest property inside the given property cache.
397 *
398 * @return IPRT status code.
399 * @param pCache Pointer to guest property cache to update user in.
400 * @param pszUser Name of guest user to update.
401 * @param pszDomain Domain of guest user to update. Optional.
402 * @param pszKey Key name of guest property to update.
403 * @param pszValueFormat Guest property value to set. Pass NULL for deleting
404 * the property.
405 */
406int VGSvcUserUpdateF(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain,
407 const char *pszKey, const char *pszValueFormat, ...)
408{
409 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
410 AssertPtrReturn(pszUser, VERR_INVALID_POINTER);
411 /* pszDomain is optional. */
412 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
413 /* pszValueFormat is optional. */
414
415 int rc = VINF_SUCCESS;
416
417 char *pszName;
418 if (pszDomain)
419 {
420/** @todo r=bird: RTStrAPrintf returns -1, not zero on failure! */
421 if (!RTStrAPrintf(&pszName, "%s%s@%s/%s", g_pszPropCacheValUser, pszUser, pszDomain, pszKey))
422 rc = VERR_NO_MEMORY;
423 }
424 else
425 {
426/** @todo r=bird: RTStrAPrintf returns -1, not zero on failure! You got it
427 * right 5 lines further down... */
428 if (!RTStrAPrintf(&pszName, "%s%s/%s", g_pszPropCacheValUser, pszUser, pszKey))
429 rc = VERR_NO_MEMORY;
430 }
431
432 char *pszValue = NULL;
433 if ( RT_SUCCESS(rc)
434 && pszValueFormat)
435 {
436 va_list va;
437 va_start(va, pszValueFormat);
438 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) < 0)
439 rc = VERR_NO_MEMORY;
440 va_end(va);
441 if ( RT_SUCCESS(rc)
442 && !pszValue)
443 rc = VERR_NO_STR_MEMORY;
444 }
445
446 if (RT_SUCCESS(rc))
447 rc = VGSvcPropCacheUpdate(pCache, pszName, pszValue);
448 if (rc == VINF_SUCCESS) /* VGSvcPropCacheUpdate will also return VINF_NO_CHANGE. */
449 {
450 /** @todo Combine updating flags w/ updating the actual value. */
451 rc = VGSvcPropCacheUpdateEntry(pCache, pszName,
452 VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT,
453 NULL /* Delete on exit */);
454 }
455
456 RTStrFree(pszValue);
457 RTStrFree(pszName);
458 return rc;
459}
460
461
462/**
463 * Writes the properties that won't change while the service is running.
464 *
465 * Errors are ignored.
466 */
467static void vgsvcVMInfoWriteFixedProperties(void)
468{
469 /*
470 * First get OS information that won't change.
471 */
472 char szInfo[256];
473 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szInfo, sizeof(szInfo));
474 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Product",
475 "%s", RT_FAILURE(rc) ? "" : szInfo);
476
477 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szInfo, sizeof(szInfo));
478 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Release",
479 "%s", RT_FAILURE(rc) ? "" : szInfo);
480
481 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szInfo, sizeof(szInfo));
482 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Version",
483 "%s", RT_FAILURE(rc) ? "" : szInfo);
484
485 rc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szInfo, sizeof(szInfo));
486 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/ServicePack",
487 "%s", RT_FAILURE(rc) ? "" : szInfo);
488
489 /*
490 * Retrieve version information about Guest Additions and installed files (components).
491 */
492 char *pszAddVer;
493 char *pszAddVerExt;
494 char *pszAddRev;
495 rc = VbglR3GetAdditionsVersion(&pszAddVer, &pszAddVerExt, &pszAddRev);
496 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Version",
497 "%s", RT_FAILURE(rc) ? "" : pszAddVer);
498 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/VersionExt",
499 "%s", RT_FAILURE(rc) ? "" : pszAddVerExt);
500 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Revision",
501 "%s", RT_FAILURE(rc) ? "" : pszAddRev);
502 if (RT_SUCCESS(rc))
503 {
504 RTStrFree(pszAddVer);
505 RTStrFree(pszAddVerExt);
506 RTStrFree(pszAddRev);
507 }
508
509#ifdef RT_OS_WINDOWS
510 /*
511 * Do windows specific properties.
512 */
513 char *pszInstDir;
514 rc = VbglR3GetAdditionsInstallationPath(&pszInstDir);
515 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/InstallDir",
516 "%s", RT_FAILURE(rc) ? "" : pszInstDir);
517 if (RT_SUCCESS(rc))
518 RTStrFree(pszInstDir);
519
520 VGSvcVMInfoWinGetComponentVersions(g_uVMInfoGuestPropSvcClientID);
521#endif
522}
523
524
525#if defined(VBOX_WITH_DBUS) && defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
526/*
527 * Simple wrapper to work around compiler-specific va_list madness.
528 */
529static dbus_bool_t vboxService_dbus_message_get_args(DBusMessage *message, DBusError *error, int first_arg_type, ...)
530{
531 va_list va;
532 va_start(va, first_arg_type);
533 dbus_bool_t ret = dbus_message_get_args_valist(message, error, first_arg_type, va);
534 va_end(va);
535 return ret;
536}
537#endif
538
539
540/**
541 * Provide information about active users.
542 */
543static int vgsvcVMInfoWriteUsers(void)
544{
545 int rc;
546 char *pszUserList = NULL;
547 uint32_t cUsersInList = 0;
548
549#ifdef RT_OS_WINDOWS
550# ifndef TARGET_NT4
551 rc = VGSvcVMInfoWinWriteUsers(&g_VMInfoPropCache, &pszUserList, &cUsersInList);
552# else
553 rc = VERR_NOT_IMPLEMENTED;
554# endif
555
556#elif defined(RT_OS_FREEBSD)
557 /** @todo FreeBSD: Port logged on user info retrieval.
558 * However, FreeBSD 9 supports utmpx, so we could use the code
559 * block below (?). */
560 rc = VERR_NOT_IMPLEMENTED;
561
562#elif defined(RT_OS_HAIKU)
563 /** @todo Haiku: Port logged on user info retrieval. */
564 rc = VERR_NOT_IMPLEMENTED;
565
566#elif defined(RT_OS_OS2)
567 /** @todo OS/2: Port logged on (LAN/local/whatever) user info retrieval. */
568 rc = VERR_NOT_IMPLEMENTED;
569
570#else
571 setutxent();
572 utmpx *ut_user;
573 uint32_t cListSize = 32;
574
575 /* Allocate a first array to hold 32 users max. */
576 char **papszUsers = (char **)RTMemAllocZ(cListSize * sizeof(char *));
577 if (papszUsers)
578 rc = VINF_SUCCESS;
579 else
580 rc = VERR_NO_MEMORY;
581
582 /* Process all entries in the utmp file.
583 * Note: This only handles */
584 while ( (ut_user = getutxent())
585 && RT_SUCCESS(rc))
586 {
587# ifdef RT_OS_DARWIN /* No ut_user->ut_session on Darwin */
588 VGSvcVerbose(4, "Found entry '%s' (type: %d, PID: %RU32)\n", ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid);
589# else
590 VGSvcVerbose(4, "Found entry '%s' (type: %d, PID: %RU32, session: %RU32)\n",
591 ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid, ut_user->ut_session);
592# endif
593 if (cUsersInList > cListSize)
594 {
595 cListSize += 32;
596 void *pvNew = RTMemRealloc(papszUsers, cListSize * sizeof(char*));
597 AssertBreakStmt(pvNew, cListSize -= 32);
598 papszUsers = (char **)pvNew;
599 }
600
601 /* Make sure we don't add user names which are not
602 * part of type USER_PROCES. */
603 if (ut_user->ut_type == USER_PROCESS) /* Regular user process. */
604 {
605 bool fFound = false;
606 for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
607 fFound = strcmp(papszUsers[i], ut_user->ut_user) == 0;
608
609 if (!fFound)
610 {
611 VGSvcVerbose(4, "Adding user '%s' (type: %d) to list\n", ut_user->ut_user, ut_user->ut_type);
612
613 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ut_user->ut_user);
614 if (RT_FAILURE(rc))
615 break;
616 cUsersInList++;
617 }
618 }
619 }
620
621# ifdef VBOX_WITH_DBUS
622# if defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
623 DBusError dbErr;
624 DBusConnection *pConnection = NULL;
625 int rc2 = RTDBusLoadLib();
626 bool fHaveLibDbus = false;
627 if (RT_SUCCESS(rc2))
628 {
629 /* Handle desktop sessions using ConsoleKit. */
630 VGSvcVerbose(4, "Checking ConsoleKit sessions ...\n");
631 fHaveLibDbus = true;
632 dbus_error_init(&dbErr);
633 pConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbErr);
634 }
635
636 if ( pConnection
637 && !dbus_error_is_set(&dbErr))
638 {
639 /* Get all available sessions. */
640/** @todo r=bird: What's the point of hardcoding things here when we've taken the pain of defining CK_XXX constants at the top of the file (or vice versa)? */
641 DBusMessage *pMsgSessions = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
642 "/org/freedesktop/ConsoleKit/Manager",
643 "org.freedesktop.ConsoleKit.Manager",
644 "GetSessions");
645 if ( pMsgSessions
646 && dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL)
647 {
648 DBusMessage *pReplySessions = dbus_connection_send_with_reply_and_block(pConnection,
649 pMsgSessions, 30 * 1000 /* 30s timeout */,
650 &dbErr);
651 if ( pReplySessions
652 && !dbus_error_is_set(&dbErr))
653 {
654 char **ppszSessions;
655 int cSessions;
656 if ( dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL
657 && vboxService_dbus_message_get_args(pReplySessions, &dbErr, DBUS_TYPE_ARRAY,
658 DBUS_TYPE_OBJECT_PATH, &ppszSessions, &cSessions,
659 DBUS_TYPE_INVALID /* Termination */))
660 {
661 VGSvcVerbose(4, "ConsoleKit: retrieved %RU16 session(s)\n", cSessions);
662
663 char **ppszCurSession = ppszSessions;
664 for (ppszCurSession; ppszCurSession && *ppszCurSession; ppszCurSession++)
665 {
666 VGSvcVerbose(4, "ConsoleKit: processing session '%s' ...\n", *ppszCurSession);
667
668 /* Only respect active sessions .*/
669 bool fActive = false;
670 DBusMessage *pMsgSessionActive = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
671 *ppszCurSession,
672 "org.freedesktop.ConsoleKit.Session",
673 "IsActive");
674 if ( pMsgSessionActive
675 && dbus_message_get_type(pMsgSessionActive) == DBUS_MESSAGE_TYPE_METHOD_CALL)
676 {
677 DBusMessage *pReplySessionActive = dbus_connection_send_with_reply_and_block(pConnection,
678 pMsgSessionActive,
679 30 * 1000 /*sec*/,
680 &dbErr);
681 if ( pReplySessionActive
682 && !dbus_error_is_set(&dbErr))
683 {
684 DBusMessageIter itMsg;
685 if ( dbus_message_iter_init(pReplySessionActive, &itMsg)
686 && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_BOOLEAN)
687 {
688 /* Get uid from message. */
689 int val;
690 dbus_message_iter_get_basic(&itMsg, &val);
691 fActive = val >= 1;
692 }
693
694 if (pReplySessionActive)
695 dbus_message_unref(pReplySessionActive);
696 }
697
698 if (pMsgSessionActive)
699 dbus_message_unref(pMsgSessionActive);
700 }
701
702 VGSvcVerbose(4, "ConsoleKit: session '%s' is %s\n",
703 *ppszCurSession, fActive ? "active" : "not active");
704
705 /* *ppszCurSession now contains the object path
706 * (e.g. "/org/freedesktop/ConsoleKit/Session1"). */
707 DBusMessage *pMsgUnixUser = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
708 *ppszCurSession,
709 "org.freedesktop.ConsoleKit.Session",
710 "GetUnixUser");
711 if ( fActive
712 && pMsgUnixUser
713 && dbus_message_get_type(pMsgUnixUser) == DBUS_MESSAGE_TYPE_METHOD_CALL)
714 {
715 DBusMessage *pReplyUnixUser = dbus_connection_send_with_reply_and_block(pConnection,
716 pMsgUnixUser,
717 30 * 1000 /* 30s timeout */,
718 &dbErr);
719 if ( pReplyUnixUser
720 && !dbus_error_is_set(&dbErr))
721 {
722 DBusMessageIter itMsg;
723 if ( dbus_message_iter_init(pReplyUnixUser, &itMsg)
724 && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_UINT32)
725 {
726 /* Get uid from message. */
727 uint32_t uid;
728 dbus_message_iter_get_basic(&itMsg, &uid);
729
730 /** @todo Add support for getting UID_MIN (/etc/login.defs on
731 * Debian). */
732 uint32_t uid_min = 1000;
733
734 /* Look up user name (realname) from uid. */
735 setpwent();
736 struct passwd *ppwEntry = getpwuid(uid);
737 if ( ppwEntry
738 && ppwEntry->pw_name)
739 {
740 if (ppwEntry->pw_uid >= uid_min /* Only respect users, not daemons etc. */)
741 {
742 VGSvcVerbose(4, "ConsoleKit: session '%s' -> %s (uid: %RU32)\n",
743 *ppszCurSession, ppwEntry->pw_name, uid);
744
745 bool fFound = false;
746 for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
747 fFound = strcmp(papszUsers[i], ppwEntry->pw_name) == 0;
748
749 if (!fFound)
750 {
751 VGSvcVerbose(4, "ConsoleKit: adding user '%s' to list\n", ppwEntry->pw_name);
752
753 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ppwEntry->pw_name);
754 if (RT_FAILURE(rc))
755 break;
756 cUsersInList++;
757 }
758 }
759 /* else silently ignore the user */
760 }
761 else
762 VGSvcError("ConsoleKit: unable to lookup user name for uid=%RU32\n", uid);
763 }
764 else
765 AssertMsgFailed(("ConsoleKit: GetUnixUser returned a wrong argument type\n"));
766 }
767
768 if (pReplyUnixUser)
769 dbus_message_unref(pReplyUnixUser);
770 }
771 else if (fActive) /* don't bitch about inactive users */
772 {
773 static int s_iBitchedAboutConsoleKit = 0;
774 if (s_iBitchedAboutConsoleKit < 1)
775 {
776 s_iBitchedAboutConsoleKit++;
777 VGSvcError("ConsoleKit: unable to retrieve user for session '%s' (msg type=%d): %s\n",
778 *ppszCurSession, dbus_message_get_type(pMsgUnixUser),
779 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
780 }
781 }
782
783 if (pMsgUnixUser)
784 dbus_message_unref(pMsgUnixUser);
785 }
786
787 dbus_free_string_array(ppszSessions);
788 }
789 else
790 VGSvcError("ConsoleKit: unable to retrieve session parameters (msg type=%d): %s\n",
791 dbus_message_get_type(pMsgSessions),
792 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
793 dbus_message_unref(pReplySessions);
794 }
795
796 if (pMsgSessions)
797 {
798 dbus_message_unref(pMsgSessions);
799 pMsgSessions = NULL;
800 }
801 }
802 else
803 {
804 static int s_iBitchedAboutConsoleKit = 0;
805 if (s_iBitchedAboutConsoleKit < 3)
806 {
807 s_iBitchedAboutConsoleKit++;
808 VGSvcError("Unable to invoke ConsoleKit (%d/3) -- maybe not installed / used? Error: %s\n",
809 s_iBitchedAboutConsoleKit,
810 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
811 }
812 }
813
814 if (pMsgSessions)
815 dbus_message_unref(pMsgSessions);
816 }
817 else
818 {
819 static int s_iBitchedAboutDBus = 0;
820 if (s_iBitchedAboutDBus < 3)
821 {
822 s_iBitchedAboutDBus++;
823 VGSvcError("Unable to connect to system D-Bus (%d/3): %s\n", s_iBitchedAboutDBus,
824 fHaveLibDbus && dbus_error_is_set(&dbErr) ? dbErr.message : "D-Bus not installed");
825 }
826 }
827
828 if ( fHaveLibDbus
829 && dbus_error_is_set(&dbErr))
830 dbus_error_free(&dbErr);
831# endif /* RT_OS_LINUX */
832# endif /* VBOX_WITH_DBUS */
833
834 /** @todo Fedora/others: Handle systemd-loginctl. */
835
836 /* Calc the string length. */
837 size_t cchUserList = 0;
838 if (RT_SUCCESS(rc))
839 for (uint32_t i = 0; i < cUsersInList; i++)
840 cchUserList += (i != 0) + strlen(papszUsers[i]);
841
842 /* Build the user list. */
843 if (cchUserList > 0)
844 {
845 if (RT_SUCCESS(rc))
846 rc = RTStrAllocEx(&pszUserList, cchUserList + 1);
847 if (RT_SUCCESS(rc))
848 {
849 char *psz = pszUserList;
850 for (uint32_t i = 0; i < cUsersInList; i++)
851 {
852 if (i != 0)
853 *psz++ = ',';
854 size_t cch = strlen(papszUsers[i]);
855 memcpy(psz, papszUsers[i], cch);
856 psz += cch;
857 }
858 *psz = '\0';
859 }
860 }
861
862 /* Cleanup. */
863 for (uint32_t i = 0; i < cUsersInList; i++)
864 RTStrFree(papszUsers[i]);
865 RTMemFree(papszUsers);
866
867 endutxent(); /* Close utmpx file. */
868#endif /* !RT_OS_WINDOWS && !RT_OS_FREEBSD && !RT_OS_HAIKU && !RT_OS_OS2 */
869
870 Assert(RT_FAILURE(rc) || cUsersInList == 0 || (pszUserList && *pszUserList));
871
872 /*
873 * If the user enumeration above failed, reset the user count to 0 except
874 * we didn't have enough memory anymore. In that case we want to preserve
875 * the previous user count in order to not confuse third party tools which
876 * rely on that count.
877 */
878 if (RT_FAILURE(rc))
879 {
880 if (rc == VERR_NO_MEMORY)
881 {
882 static int s_iVMInfoBitchedOOM = 0;
883 if (s_iVMInfoBitchedOOM++ < 3)
884 VGSvcVerbose(0, "Warning: Not enough memory available to enumerate users! Keeping old value (%RU32)\n",
885 g_cVMInfoLoggedInUsers);
886 cUsersInList = g_cVMInfoLoggedInUsers;
887 }
888 else
889 cUsersInList = 0;
890 }
891 else /* Preserve logged in users count. */
892 g_cVMInfoLoggedInUsers = cUsersInList;
893
894 VGSvcVerbose(4, "cUsersInList=%RU32, pszUserList=%s, rc=%Rrc\n", cUsersInList, pszUserList ? pszUserList : "<NULL>", rc);
895
896 if (pszUserList)
897 {
898 AssertMsg(cUsersInList, ("pszUserList contains users whereas cUsersInList is 0\n"));
899 rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, "%s", pszUserList);
900 }
901 else
902 rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, NULL);
903 if (RT_FAILURE(rc))
904 VGSvcError("Error writing logged in users list, rc=%Rrc\n", rc);
905
906 rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers, "%RU32", cUsersInList);
907 if (RT_FAILURE(rc))
908 VGSvcError("Error writing logged in users count, rc=%Rrc\n", rc);
909
910/** @todo r=bird: What's this 'beacon' nonsense here? It's _not_ defined with
911 * the VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE flag set!! */
912 rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers, cUsersInList == 0 ? "true" : "false");
913 if (RT_FAILURE(rc))
914 VGSvcError("Error writing no logged in users beacon, rc=%Rrc\n", rc);
915
916 if (pszUserList)
917 RTStrFree(pszUserList);
918
919 VGSvcVerbose(4, "Writing users returned with rc=%Rrc\n", rc);
920 return rc;
921}
922
923
924/**
925 * Provide information about the guest network.
926 */
927static int vgsvcVMInfoWriteNetwork(void)
928{
929 int rc = VINF_SUCCESS;
930 uint32_t cIfsReported = 0;
931 char szPropPath[256];
932
933#ifdef RT_OS_WINDOWS
934 IP_ADAPTER_INFO *pAdpInfo = NULL;
935
936# ifndef TARGET_NT4
937 ULONG cbAdpInfo = sizeof(*pAdpInfo);
938 pAdpInfo = (IP_ADAPTER_INFO *)RTMemAlloc(cbAdpInfo);
939 if (!pAdpInfo)
940 {
941 VGSvcError("VMInfo/Network: Failed to allocate IP_ADAPTER_INFO\n");
942 return VERR_NO_MEMORY;
943 }
944 DWORD dwRet = GetAdaptersInfo(pAdpInfo, &cbAdpInfo);
945 if (dwRet == ERROR_BUFFER_OVERFLOW)
946 {
947 IP_ADAPTER_INFO *pAdpInfoNew = (IP_ADAPTER_INFO*)RTMemRealloc(pAdpInfo, cbAdpInfo);
948 if (pAdpInfoNew)
949 {
950 pAdpInfo = pAdpInfoNew;
951 dwRet = GetAdaptersInfo(pAdpInfo, &cbAdpInfo);
952 }
953 }
954 else if (dwRet == ERROR_NO_DATA)
955 {
956 VGSvcVerbose(3, "VMInfo/Network: No network adapters available\n");
957
958 /* If no network adapters available / present in the
959 * system we pretend success to not bail out too early. */
960 dwRet = ERROR_SUCCESS;
961 }
962
963 if (dwRet != ERROR_SUCCESS)
964 {
965 if (pAdpInfo)
966 RTMemFree(pAdpInfo);
967 VGSvcError("VMInfo/Network: Failed to get adapter info: Error %d\n", dwRet);
968 return RTErrConvertFromWin32(dwRet);
969 }
970# endif /* !TARGET_NT4 */
971
972 SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
973 if (sd == SOCKET_ERROR) /* Socket invalid. */
974 {
975 int wsaErr = WSAGetLastError();
976 /* Don't complain/bail out with an error if network stack is not up; can happen
977 * on NT4 due to start up when not connected shares dialogs pop up. */
978 if (WSAENETDOWN == wsaErr)
979 {
980 VGSvcVerbose(0, "VMInfo/Network: Network is not up yet.\n");
981 wsaErr = VINF_SUCCESS;
982 }
983 else
984 VGSvcError("VMInfo/Network: Failed to get a socket: Error %d\n", wsaErr);
985 if (pAdpInfo)
986 RTMemFree(pAdpInfo);
987 return RTErrConvertFromWin32(wsaErr);
988 }
989
990 INTERFACE_INFO aInterfaces[20] = {0};
991 DWORD cbReturned = 0;
992# ifdef TARGET_NT4
993 /* Workaround for uninitialized variable used in memcpy in GetTcpipInterfaceList
994 (NT4SP1 at least). It seems to be happy enough with garbages, no failure
995 returns so far, so we just need to prevent it from crashing by filling the
996 stack with valid pointer values prior to the API call. */
997 _asm
998 {
999 mov edx, edi
1000 lea eax, aInterfaces
1001 mov [esp - 0x1000], eax
1002 mov [esp - 0x2000], eax
1003 mov ecx, 0x2000/4 - 1
1004 cld
1005 lea edi, [esp - 0x2000]
1006 rep stosd
1007 mov edi, edx
1008 }
1009# endif
1010 if ( WSAIoctl(sd,
1011 SIO_GET_INTERFACE_LIST,
1012 NULL, /* pvInBuffer */
1013 0, /* cbInBuffer */
1014 &aInterfaces[0], /* pvOutBuffer */
1015 sizeof(aInterfaces), /* cbOutBuffer */
1016 &cbReturned,
1017 NULL, /* pOverlapped */
1018 NULL) /* pCompletionRoutine */
1019 == SOCKET_ERROR)
1020 {
1021 VGSvcError("VMInfo/Network: Failed to WSAIoctl() on socket: Error: %d\n", WSAGetLastError());
1022 if (pAdpInfo)
1023 RTMemFree(pAdpInfo);
1024 return RTErrConvertFromWin32(WSAGetLastError());
1025 }
1026 int cIfacesSystem = cbReturned / sizeof(INTERFACE_INFO);
1027
1028 /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
1029 for (int i = 0; i < cIfacesSystem; ++i)
1030 {
1031 sockaddr_in *pAddress;
1032 u_long nFlags = 0;
1033 if (aInterfaces[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
1034 continue;
1035 nFlags = aInterfaces[i].iiFlags;
1036 pAddress = (sockaddr_in *)&(aInterfaces[i].iiAddress);
1037 Assert(pAddress);
1038 char szIp[32];
1039 RTStrPrintf(szIp, sizeof(szIp), "%s", inet_ntoa(pAddress->sin_addr));
1040 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfsReported);
1041 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szIp);
1042
1043 pAddress = (sockaddr_in *) & (aInterfaces[i].iiBroadcastAddress);
1044 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfsReported);
1045 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1046
1047 pAddress = (sockaddr_in *)&(aInterfaces[i].iiNetmask);
1048 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfsReported);
1049 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1050
1051 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfsReported);
1052 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, nFlags & IFF_UP ? "Up" : "Down");
1053
1054# ifndef TARGET_NT4
1055 IP_ADAPTER_INFO *pAdp;
1056 for (pAdp = pAdpInfo; pAdp; pAdp = pAdp->Next)
1057 if (!strcmp(pAdp->IpAddressList.IpAddress.String, szIp))
1058 break;
1059
1060 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfsReported);
1061 if (pAdp)
1062 {
1063 char szMac[32];
1064 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
1065 pAdp->Address[0], pAdp->Address[1], pAdp->Address[2],
1066 pAdp->Address[3], pAdp->Address[4], pAdp->Address[5]);
1067 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
1068 }
1069 else
1070 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, NULL);
1071# endif /* !TARGET_NT4 */
1072
1073 cIfsReported++;
1074 }
1075 if (pAdpInfo)
1076 RTMemFree(pAdpInfo);
1077 if (sd >= 0)
1078 closesocket(sd);
1079
1080#elif defined(RT_OS_HAIKU)
1081 /** @todo Haiku: implement network info. retreival */
1082 return VERR_NOT_IMPLEMENTED;
1083
1084#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1085 struct ifaddrs *pIfHead = NULL;
1086
1087 /* Get all available interfaces */
1088 rc = getifaddrs(&pIfHead);
1089 if (rc < 0)
1090 {
1091 rc = RTErrConvertFromErrno(errno);
1092 VGSvcError("VMInfo/Network: Failed to get all interfaces: Error %Rrc\n");
1093 return rc;
1094 }
1095
1096 /* Loop through all interfaces and set the data. */
1097 for (struct ifaddrs *pIfCurr = pIfHead; pIfCurr; pIfCurr = pIfCurr->ifa_next)
1098 {
1099 /*
1100 * Only AF_INET and no loopback interfaces
1101 */
1102 /** @todo: IPv6 interfaces */
1103 if ( pIfCurr->ifa_addr->sa_family == AF_INET
1104 && !(pIfCurr->ifa_flags & IFF_LOOPBACK))
1105 {
1106 char szInetAddr[NI_MAXHOST];
1107
1108 memset(szInetAddr, 0, NI_MAXHOST);
1109 getnameinfo(pIfCurr->ifa_addr, sizeof(struct sockaddr_in),
1110 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1111 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfsReported);
1112 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1113
1114 memset(szInetAddr, 0, NI_MAXHOST);
1115 getnameinfo(pIfCurr->ifa_broadaddr, sizeof(struct sockaddr_in),
1116 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1117 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfsReported);
1118 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1119
1120 memset(szInetAddr, 0, NI_MAXHOST);
1121 getnameinfo(pIfCurr->ifa_netmask, sizeof(struct sockaddr_in),
1122 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1123 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfsReported);
1124 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1125
1126 /* Search for the AF_LINK interface of the current AF_INET one and get the mac. */
1127 for (struct ifaddrs *pIfLinkCurr = pIfHead; pIfLinkCurr; pIfLinkCurr = pIfLinkCurr->ifa_next)
1128 {
1129 if ( pIfLinkCurr->ifa_addr->sa_family == AF_LINK
1130 && !strcmp(pIfCurr->ifa_name, pIfLinkCurr->ifa_name))
1131 {
1132 char szMac[32];
1133 uint8_t *pu8Mac = NULL;
1134 struct sockaddr_dl *pLinkAddress = (struct sockaddr_dl *)pIfLinkCurr->ifa_addr;
1135
1136 AssertPtr(pLinkAddress);
1137 pu8Mac = (uint8_t *)LLADDR(pLinkAddress);
1138 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
1139 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
1140 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfsReported);
1141 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
1142 break;
1143 }
1144 }
1145
1146 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfsReported);
1147 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down");
1148
1149 cIfsReported++;
1150 }
1151 }
1152
1153 /* Free allocated resources. */
1154 freeifaddrs(pIfHead);
1155
1156#else /* !RT_OS_WINDOWS && !RT_OS_FREEBSD */
1157 /*
1158 * Use SIOCGIFCONF to get a list of interface/protocol configurations.
1159 *
1160 * See "UNIX Network Programming Volume 1" by W. R. Stevens, section 17.6
1161 * for details on this ioctl.
1162 */
1163 int sd = socket(AF_INET, SOCK_DGRAM, 0);
1164 if (sd < 0)
1165 {
1166 rc = RTErrConvertFromErrno(errno);
1167 VGSvcError("VMInfo/Network: Failed to get a socket: Error %Rrc\n", rc);
1168 return rc;
1169 }
1170
1171 /* Call SIOCGIFCONF with the right sized buffer (remember the size). */
1172 static int s_cbBuf = 256; // 1024
1173 int cbBuf = s_cbBuf;
1174 char *pchBuf;
1175 struct ifconf IfConf;
1176 rc = VINF_SUCCESS;
1177 for (;;)
1178 {
1179 pchBuf = (char *)RTMemTmpAllocZ(cbBuf);
1180 if (!pchBuf)
1181 {
1182 rc = VERR_NO_TMP_MEMORY;
1183 break;
1184 }
1185
1186 IfConf.ifc_len = cbBuf;
1187 IfConf.ifc_buf = pchBuf;
1188 if (ioctl(sd, SIOCGIFCONF, &IfConf) >= 0)
1189 {
1190 /* Hard to anticipate how space an address might possibly take, so
1191 making some generous assumptions here to avoid performing the
1192 query twice with different buffer sizes. */
1193 if (IfConf.ifc_len + 128 < cbBuf)
1194 break;
1195 }
1196 else if (errno != EOVERFLOW)
1197 {
1198 rc = RTErrConvertFromErrno(errno);
1199 break;
1200 }
1201
1202 /* grow the buffer */
1203 s_cbBuf = cbBuf *= 2;
1204 RTMemFree(pchBuf);
1205 }
1206 if (RT_FAILURE(rc))
1207 {
1208 close(sd);
1209 RTMemTmpFree(pchBuf);
1210 VGSvcError("VMInfo/Network: Error doing SIOCGIFCONF (cbBuf=%d): %Rrc\n", cbBuf, rc);
1211 return rc;
1212 }
1213
1214 /*
1215 * Iterate the interface/protocol configurations.
1216 *
1217 * Note! The current code naively assumes one IPv4 address per interface.
1218 * This means that guest assigning more than one address to an
1219 * interface will get multiple entries for one physical interface.
1220 */
1221# ifdef RT_OS_OS2
1222 struct ifreq *pPrevLinkAddr = NULL;
1223# endif
1224 struct ifreq *pCur = IfConf.ifc_req;
1225 size_t cbLeft = IfConf.ifc_len;
1226 while (cbLeft >= sizeof(*pCur))
1227 {
1228# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
1229 /* These two do not provide the sa_len member but only support address
1230 * families which do not need extra bytes on the end. */
1231# define SA_LEN(pAddr) sizeof(struct sockaddr)
1232# elif !defined(SA_LEN)
1233# define SA_LEN(pAddr) (pAddr)->sa_len
1234# endif
1235 /* Figure the size of the current request. */
1236 size_t cbCur = RT_OFFSETOF(struct ifreq, ifr_addr)
1237 + SA_LEN(&pCur->ifr_addr);
1238 cbCur = RT_MAX(cbCur, sizeof(struct ifreq));
1239# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
1240 Assert(pCur->ifr_addr.sa_family == AF_INET);
1241# endif
1242 AssertBreak(cbCur <= cbLeft);
1243
1244# ifdef RT_OS_OS2
1245 /* On OS/2 we get the MAC address in the AF_LINK that the BSD 4.4 stack
1246 emits. We boldly ASSUME these always comes first. */
1247 if ( pCur->ifr_addr.sa_family == AF_LINK
1248 && ((struct sockaddr_dl *)&pCur->ifr_addr)->sdl_alen == 6)
1249 pPrevLinkAddr = pCur;
1250# endif
1251
1252 /* Skip it if it's not the kind of address we're looking for. */
1253 struct ifreq IfReqTmp;
1254 bool fIfUp = false;
1255 bool fSkip = false;
1256 if (pCur->ifr_addr.sa_family != AF_INET)
1257 fSkip = true;
1258 else
1259 {
1260 /* Get the interface flags so we can detect loopback and check if it's up. */
1261 IfReqTmp = *pCur;
1262 if (ioctl(sd, SIOCGIFFLAGS, &IfReqTmp) < 0)
1263 {
1264 rc = RTErrConvertFromErrno(errno);
1265 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFFLAGS,%s) on socket: Error %Rrc\n", pCur->ifr_name, rc);
1266 break;
1267 }
1268 fIfUp = !!(IfReqTmp.ifr_flags & IFF_UP);
1269 if (IfReqTmp.ifr_flags & IFF_LOOPBACK) /* Skip the loopback device. */
1270 fSkip = true;
1271 }
1272 if (!fSkip)
1273 {
1274 size_t offSubProp = RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32", cIfsReported);
1275
1276 sockaddr_in *pAddress = (sockaddr_in *)&pCur->ifr_addr;
1277 strcpy(&szPropPath[offSubProp], "/V4/IP");
1278 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1279
1280 /* Get the broadcast address. */
1281 IfReqTmp = *pCur;
1282 if (ioctl(sd, SIOCGIFBRDADDR, &IfReqTmp) < 0)
1283 {
1284 rc = RTErrConvertFromErrno(errno);
1285 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %Rrc\n", rc);
1286 break;
1287 }
1288 pAddress = (sockaddr_in *)&IfReqTmp.ifr_broadaddr;
1289 strcpy(&szPropPath[offSubProp], "/V4/Broadcast");
1290 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1291
1292 /* Get the net mask. */
1293 IfReqTmp = *pCur;
1294 if (ioctl(sd, SIOCGIFNETMASK, &IfReqTmp) < 0)
1295 {
1296 rc = RTErrConvertFromErrno(errno);
1297 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFNETMASK) on socket: Error %Rrc\n", rc);
1298 break;
1299 }
1300# if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
1301 pAddress = (sockaddr_in *)&IfReqTmp.ifr_addr;
1302# else
1303 pAddress = (sockaddr_in *)&IfReqTmp.ifr_netmask;
1304# endif
1305 strcpy(&szPropPath[offSubProp], "/V4/Netmask");
1306 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1307
1308# if defined(RT_OS_SOLARIS)
1309 /*
1310 * "ifreq" is obsolete on Solaris. We use the recommended "lifreq".
1311 * We might fail if the interface has not been assigned an IP address.
1312 * That doesn't matter; as long as it's plumbed we can pick it up.
1313 * But, if it has not acquired an IP address we cannot obtain it's MAC
1314 * address this way, so we just use all zeros there.
1315 */
1316 RTMAC IfMac;
1317 struct lifreq IfReq;
1318 RT_ZERO(IfReq);
1319 AssertCompile(sizeof(IfReq.lifr_name) >= sizeof(pCur->ifr_name));
1320 strncpy(IfReq.lifr_name, pCur->ifr_name, sizeof(pCur->ifr_name));
1321 if (ioctl(sd, SIOCGLIFADDR, &IfReq) >= 0)
1322 {
1323 struct arpreq ArpReq;
1324 RT_ZERO(ArpReq);
1325 memcpy(&ArpReq.arp_pa, &IfReq.lifr_addr, sizeof(struct sockaddr_in));
1326
1327 if (ioctl(sd, SIOCGARP, &ArpReq) >= 0)
1328 memcpy(&IfMac, ArpReq.arp_ha.sa_data, sizeof(IfMac));
1329 else
1330 {
1331 rc = RTErrConvertFromErrno(errno);
1332 VGSvcError("VMInfo/Network: failed to ioctl(SIOCGARP) on socket: Error %Rrc\n", rc);
1333 break;
1334 }
1335 }
1336 else
1337 {
1338 VGSvcVerbose(2, "VMInfo/Network: Interface '%s' has no assigned IP address, skipping ...\n", pCur->ifr_name);
1339 continue;
1340 }
1341# elif defined(RT_OS_OS2)
1342 RTMAC IfMac;
1343 if ( pPrevLinkAddr
1344 && strncmp(pCur->ifr_name, pPrevLinkAddr->ifr_name, sizeof(pCur->ifr_name)) == 0)
1345 {
1346 struct sockaddr_dl *pDlAddr = (struct sockaddr_dl *)&pPrevLinkAddr->ifr_addr;
1347 IfMac = *(PRTMAC)&pDlAddr->sdl_data[pDlAddr->sdl_nlen];
1348 }
1349 else
1350 RT_ZERO(IfMac);
1351#else
1352 if (ioctl(sd, SIOCGIFHWADDR, &IfReqTmp) < 0)
1353 {
1354 rc = RTErrConvertFromErrno(errno);
1355 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFHWADDR) on socket: Error %Rrc\n", rc);
1356 break;
1357 }
1358 RTMAC IfMac = *(PRTMAC)&IfReqTmp.ifr_hwaddr.sa_data[0];
1359# endif
1360 strcpy(&szPropPath[offSubProp], "/MAC");
1361 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%02X%02X%02X%02X%02X%02X",
1362 IfMac.au8[0], IfMac.au8[1], IfMac.au8[2], IfMac.au8[3], IfMac.au8[4], IfMac.au8[5]);
1363
1364 strcpy(&szPropPath[offSubProp], "/Status");
1365 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, fIfUp ? "Up" : "Down");
1366
1367 /* The name. */
1368 int rc2 = RTStrValidateEncodingEx(pCur->ifr_name, sizeof(pCur->ifr_name), 0);
1369 if (RT_SUCCESS(rc2))
1370 {
1371 strcpy(&szPropPath[offSubProp], "/Name");
1372 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%.*s", sizeof(pCur->ifr_name), pCur->ifr_name);
1373 }
1374
1375 cIfsReported++;
1376 }
1377
1378 /*
1379 * Next interface/protocol configuration.
1380 */
1381 pCur = (struct ifreq *)((uintptr_t)pCur + cbCur);
1382 cbLeft -= cbCur;
1383 }
1384
1385 RTMemTmpFree(pchBuf);
1386 close(sd);
1387 if (RT_FAILURE(rc))
1388 VGSvcError("VMInfo/Network: Network enumeration for interface %RU32 failed with error %Rrc\n", cIfsReported, rc);
1389
1390#endif /* !RT_OS_WINDOWS */
1391
1392#if 0 /* Zapping not enabled yet, needs more testing first. */
1393 /*
1394 * Zap all stale network interface data if the former (saved) network ifaces count
1395 * is bigger than the current one.
1396 */
1397
1398 /* Get former count. */
1399 uint32_t cIfsReportedOld;
1400 rc = VGSvcReadPropUInt32(g_uVMInfoGuestPropSvcClientID, g_pszPropCacheValNetCount, &cIfsReportedOld,
1401 0 /* Min */, UINT32_MAX /* Max */);
1402 if ( RT_SUCCESS(rc)
1403 && cIfsReportedOld > cIfsReported) /* Are some ifaces not around anymore? */
1404 {
1405 VGSvcVerbose(3, "VMInfo/Network: Stale interface data detected (%RU32 old vs. %RU32 current)\n",
1406 cIfsReportedOld, cIfsReported);
1407
1408 uint32_t uIfaceDeleteIdx = cIfsReported;
1409 do
1410 {
1411 VGSvcVerbose(3, "VMInfo/Network: Deleting stale data of interface %d ...\n", uIfaceDeleteIdx);
1412 rc = VGSvcPropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%RU32", uIfaceDeleteIdx++);
1413 } while (RT_SUCCESS(rc));
1414 }
1415 else if ( RT_FAILURE(rc)
1416 && rc != VERR_NOT_FOUND)
1417 {
1418 VGSvcError("VMInfo/Network: Failed retrieving old network interfaces count with error %Rrc\n", rc);
1419 }
1420#endif
1421
1422 /*
1423 * This property is a beacon which is _always_ written, even if the network configuration
1424 * does not change. If this property is missing, the host assumes that all other GuestInfo
1425 * properties are no longer valid.
1426 */
1427 VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNetCount, "%RU32", cIfsReported);
1428
1429 /* Don't fail here; just report everything we got. */
1430 return VINF_SUCCESS;
1431}
1432
1433
1434/**
1435 * @interface_method_impl{VBOXSERVICE,pfnWorker}
1436 */
1437static DECLCALLBACK(int) vbsvcVMInfoWorker(bool volatile *pfShutdown)
1438{
1439 int rc;
1440
1441 /*
1442 * Tell the control thread that it can continue
1443 * spawning services.
1444 */
1445 RTThreadUserSignal(RTThreadSelf());
1446
1447#ifdef RT_OS_WINDOWS
1448 /* Required for network information (must be called per thread). */
1449 WSADATA wsaData;
1450 if (WSAStartup(MAKEWORD(2, 2), &wsaData))
1451 VGSvcError("VMInfo/Network: WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(WSAGetLastError()));
1452#endif /* RT_OS_WINDOWS */
1453
1454 /*
1455 * Write the fixed properties first.
1456 */
1457 vgsvcVMInfoWriteFixedProperties();
1458
1459 /*
1460 * Now enter the loop retrieving runtime data continuously.
1461 */
1462 for (;;)
1463 {
1464 rc = vgsvcVMInfoWriteUsers();
1465 if (RT_FAILURE(rc))
1466 break;
1467
1468 rc = vgsvcVMInfoWriteNetwork();
1469 if (RT_FAILURE(rc))
1470 break;
1471
1472 /* Whether to wait for event semaphore or not. */
1473 bool fWait = true;
1474
1475 /* Check for location awareness. This most likely only
1476 * works with VBox (latest) 4.1 and up. */
1477
1478 /* Check for new connection. */
1479 char *pszLAClientID = NULL;
1480 int rc2 = VGSvcReadHostProp(g_uVMInfoGuestPropSvcClientID, g_pszLAActiveClient, true /* Read only */,
1481 &pszLAClientID, NULL /* Flags */, NULL /* Timestamp */);
1482 if (RT_SUCCESS(rc2))
1483 {
1484 AssertPtr(pszLAClientID);
1485 if (RTStrICmp(pszLAClientID, "0")) /* Is a client connected? */
1486 {
1487 uint32_t uLAClientID = RTStrToInt32(pszLAClientID);
1488 uint64_t uLAClientAttachedTS;
1489
1490 /* Peek at "Attach" value to figure out if hotdesking happened. */
1491 char *pszAttach = NULL;
1492 rc2 = vgsvcGetLAClientValue(uLAClientID, "Attach", &pszAttach,
1493 &uLAClientAttachedTS);
1494
1495 if ( RT_SUCCESS(rc2)
1496 && ( !g_LAClientAttachedTS
1497 || (g_LAClientAttachedTS != uLAClientAttachedTS)))
1498 {
1499 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1500
1501 /* Note: There is a race between setting the guest properties by the host and getting them by
1502 * the guest. */
1503 rc2 = vgsvcGetLAClientInfo(uLAClientID, &g_LAClientInfo);
1504 if (RT_SUCCESS(rc2))
1505 {
1506 VGSvcVerbose(1, "VRDP: Hotdesk client %s with ID=%RU32, Name=%s, Domain=%s\n",
1507 /* If g_LAClientAttachedTS is 0 this means there already was an active
1508 * hotdesk session when VBoxService started. */
1509 !g_LAClientAttachedTS ? "already active" : g_LAClientInfo.fAttached ? "connected" : "disconnected",
1510 uLAClientID, g_LAClientInfo.pszName, g_LAClientInfo.pszDomain);
1511
1512 g_LAClientAttachedTS = g_LAClientInfo.uAttachedTS;
1513
1514 /* Don't wait for event semaphore below anymore because we now know that the client
1515 * changed. This means we need to iterate all VM information again immediately. */
1516 fWait = false;
1517 }
1518 else
1519 {
1520 static int s_iBitchedAboutLAClientInfo = 0;
1521 if (s_iBitchedAboutLAClientInfo < 10)
1522 {
1523 s_iBitchedAboutLAClientInfo++;
1524 VGSvcError("Error getting active location awareness client info, rc=%Rrc\n", rc2);
1525 }
1526 }
1527 }
1528 else if (RT_FAILURE(rc2))
1529 VGSvcError("Error getting attached value of location awareness client %RU32, rc=%Rrc\n", uLAClientID, rc2);
1530 if (pszAttach)
1531 RTStrFree(pszAttach);
1532 }
1533 else
1534 {
1535 VGSvcVerbose(1, "VRDP: UTTSC disconnected from VRDP server\n");
1536 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1537 }
1538
1539 RTStrFree(pszLAClientID);
1540 }
1541 else
1542 {
1543 static int s_iBitchedAboutLAClient = 0;
1544 if ( (rc2 != VERR_NOT_FOUND) /* No location awareness installed, skip. */
1545 && s_iBitchedAboutLAClient < 3)
1546 {
1547 s_iBitchedAboutLAClient++;
1548 VGSvcError("VRDP: Querying connected location awareness client failed with rc=%Rrc\n", rc2);
1549 }
1550 }
1551
1552 VGSvcVerbose(3, "VRDP: Handling location awareness done\n");
1553
1554 /*
1555 * Flush all properties if we were restored.
1556 */
1557 uint64_t idNewSession = g_idVMInfoSession;
1558 VbglR3GetSessionId(&idNewSession);
1559 if (idNewSession != g_idVMInfoSession)
1560 {
1561 VGSvcVerbose(3, "The VM session ID changed, flushing all properties\n");
1562 vgsvcVMInfoWriteFixedProperties();
1563 VGSvcPropCacheFlush(&g_VMInfoPropCache);
1564 g_idVMInfoSession = idNewSession;
1565 }
1566
1567 /*
1568 * Block for a while.
1569 *
1570 * The event semaphore takes care of ignoring interruptions and it
1571 * allows us to implement service wakeup later.
1572 */
1573 if (*pfShutdown)
1574 break;
1575 if (fWait)
1576 rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval);
1577 if (*pfShutdown)
1578 break;
1579 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
1580 {
1581 VGSvcError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
1582 rc = rc2;
1583 break;
1584 }
1585 else if (RT_LIKELY(RT_SUCCESS(rc2)))
1586 {
1587 /* Reset event semaphore if it got triggered. */
1588 rc2 = RTSemEventMultiReset(g_hVMInfoEvent);
1589 if (RT_FAILURE(rc2))
1590 rc2 = VGSvcError("RTSemEventMultiReset failed; rc2=%Rrc\n", rc2);
1591 }
1592 }
1593
1594#ifdef RT_OS_WINDOWS
1595 WSACleanup();
1596#endif
1597
1598 return rc;
1599}
1600
1601
1602/**
1603 * @interface_method_impl{VBOXSERVICE,pfnStop}
1604 */
1605static DECLCALLBACK(void) vbsvcVMInfoStop(void)
1606{
1607 RTSemEventMultiSignal(g_hVMInfoEvent);
1608}
1609
1610
1611/**
1612 * @interface_method_impl{VBOXSERVICE,pfnTerm}
1613 */
1614static DECLCALLBACK(void) vbsvcVMInfoTerm(void)
1615{
1616 if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
1617 {
1618 /** @todo temporary solution: Zap all values which are not valid
1619 * anymore when VM goes down (reboot/shutdown ). Needs to
1620 * be replaced with "temporary properties" later.
1621 *
1622 * One idea is to introduce a (HGCM-)session guest property
1623 * flag meaning that a guest property is only valid as long
1624 * as the HGCM session isn't closed (e.g. guest application
1625 * terminates). [don't remove till implemented]
1626 */
1627 /** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and use the cache
1628 * since it remembers what we've written. */
1629 /* Delete the "../Net" branch. */
1630 const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
1631 int rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
1632
1633 /* Destroy LA client info. */
1634 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1635
1636 /* Destroy property cache. */
1637 VGSvcPropCacheDestroy(&g_VMInfoPropCache);
1638
1639 /* Disconnect from guest properties service. */
1640 rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID);
1641 if (RT_FAILURE(rc))
1642 VGSvcError("Failed to disconnect from guest property service! Error: %Rrc\n", rc);
1643 g_uVMInfoGuestPropSvcClientID = 0;
1644
1645 RTSemEventMultiDestroy(g_hVMInfoEvent);
1646 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
1647 }
1648}
1649
1650
1651/**
1652 * The 'vminfo' service description.
1653 */
1654VBOXSERVICE g_VMInfo =
1655{
1656 /* pszName. */
1657 "vminfo",
1658 /* pszDescription. */
1659 "Virtual Machine Information",
1660 /* pszUsage. */
1661 " [--vminfo-interval <ms>] [--vminfo-user-idle-threshold <ms>]"
1662 ,
1663 /* pszOptions. */
1664 " --vminfo-interval Specifies the interval at which to retrieve the\n"
1665 " VM information. The default is 10000 ms.\n"
1666 " --vminfo-user-idle-threshold <ms>\n"
1667 " Specifies the user idle threshold (in ms) for\n"
1668 " considering a guest user as being idle. The default\n"
1669 " is 5000 (5 seconds).\n"
1670 ,
1671 /* methods */
1672 vbsvcVMInfoPreInit,
1673 vbsvcVMInfoOption,
1674 vbsvcVMInfoInit,
1675 vbsvcVMInfoWorker,
1676 vbsvcVMInfoStop,
1677 vbsvcVMInfoTerm
1678};
1679
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