VirtualBox

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

Last change on this file since 96598 was 96598, checked in by vboxsync, 2 years ago

VBoxService/vminfo: Corrected API requirement checks in vgsvcVMInfoWriteNetwork - the code only requires the TCP/IP ones, not the GetAdaptersInfo one. Only allocate buffer for and try call GetAdaptersInfo if we've got a valid pointer to it (shuts up confusing/noisy messages on NT4). Did some futher cleanups, as per usual.

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