VirtualBox

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

Last change on this file since 63566 was 63566, checked in by vboxsync, 8 years ago

scm: cleaning up todos

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