VirtualBox

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

Last change on this file since 70173 was 70173, checked in by vboxsync, 7 years ago

VBoxServiceNT: Temporarily a duplicate of VBoxService.

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