VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use