VirtualBox

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

Last change on this file since 98831 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.0 KB
Line 
1/* $Id: VBoxServiceVMInfo.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host.
4 */
5
6/*
7 * Copyright (C) 2009-2023 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 if (RTStrAPrintf(&pszName, "%s%s@%s/%s", g_pszPropCacheValUser, pszUser, pszDomain, pszKey) < 0)
437 rc = VERR_NO_MEMORY;
438 }
439 else
440 {
441 if (RTStrAPrintf(&pszName, "%s%s/%s", g_pszPropCacheValUser, pszUser, pszKey) < 0)
442 rc = VERR_NO_MEMORY;
443 }
444
445 char *pszValue = NULL;
446 if ( RT_SUCCESS(rc)
447 && pszValueFormat)
448 {
449 va_list va;
450 va_start(va, pszValueFormat);
451 if (RTStrAPrintfV(&pszValue, pszValueFormat, va) < 0)
452 rc = VERR_NO_MEMORY;
453 va_end(va);
454 if ( RT_SUCCESS(rc)
455 && !pszValue)
456 rc = VERR_NO_STR_MEMORY;
457 }
458
459 if (RT_SUCCESS(rc))
460 rc = VGSvcPropCacheUpdate(pCache, pszName, pszValue);
461 if (rc == VINF_SUCCESS) /* VGSvcPropCacheUpdate will also return VINF_NO_CHANGE. */
462 {
463 /** @todo Combine updating flags w/ updating the actual value. */
464 rc = VGSvcPropCacheUpdateEntry(pCache, pszName,
465 VGSVCPROPCACHE_FLAGS_TEMPORARY | VGSVCPROPCACHE_FLAGS_TRANSIENT,
466 NULL /* Delete on exit */);
467 }
468
469 RTStrFree(pszValue);
470 RTStrFree(pszName);
471 return rc;
472}
473
474
475/**
476 * Writes the properties that won't change while the service is running.
477 *
478 * Errors are ignored.
479 */
480static void vgsvcVMInfoWriteFixedProperties(void)
481{
482 /*
483 * First get OS information that won't change.
484 */
485 char szInfo[256];
486 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szInfo, sizeof(szInfo));
487 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Product",
488 "%s", RT_FAILURE(rc) ? "" : szInfo);
489
490 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szInfo, sizeof(szInfo));
491 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Release",
492 "%s", RT_FAILURE(rc) ? "" : szInfo);
493
494 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szInfo, sizeof(szInfo));
495 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Version",
496 "%s", RT_FAILURE(rc) ? "" : szInfo);
497
498 rc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szInfo, sizeof(szInfo));
499 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/ServicePack",
500 "%s", RT_FAILURE(rc) ? "" : szInfo);
501
502 /*
503 * Retrieve version information about Guest Additions and installed files (components).
504 */
505 char *pszAddVer;
506 char *pszAddVerExt;
507 char *pszAddRev;
508 rc = VbglR3GetAdditionsVersion(&pszAddVer, &pszAddVerExt, &pszAddRev);
509 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Version",
510 "%s", RT_FAILURE(rc) ? "" : pszAddVer);
511 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/VersionExt",
512 "%s", RT_FAILURE(rc) ? "" : pszAddVerExt);
513 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Revision",
514 "%s", RT_FAILURE(rc) ? "" : pszAddRev);
515 if (RT_SUCCESS(rc))
516 {
517 RTStrFree(pszAddVer);
518 RTStrFree(pszAddVerExt);
519 RTStrFree(pszAddRev);
520 }
521
522#ifdef RT_OS_WINDOWS
523 /*
524 * Do windows specific properties.
525 */
526 char *pszInstDir;
527 rc = VbglR3GetAdditionsInstallationPath(&pszInstDir);
528 VGSvcWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/InstallDir",
529 "%s", RT_FAILURE(rc) ? "" : pszInstDir);
530 if (RT_SUCCESS(rc))
531 RTStrFree(pszInstDir);
532
533 VGSvcVMInfoWinGetComponentVersions(g_uVMInfoGuestPropSvcClientID);
534#endif
535}
536
537
538#if defined(VBOX_WITH_DBUS) && defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
539/*
540 * Simple wrapper to work around compiler-specific va_list madness.
541 */
542static dbus_bool_t vboxService_dbus_message_get_args(DBusMessage *message, DBusError *error, int first_arg_type, ...)
543{
544 va_list va;
545 va_start(va, first_arg_type);
546 dbus_bool_t ret = dbus_message_get_args_valist(message, error, first_arg_type, va);
547 va_end(va);
548 return ret;
549}
550#endif
551
552
553/**
554 * Provide information about active users.
555 */
556static int vgsvcVMInfoWriteUsers(void)
557{
558 int rc;
559 char *pszUserList = NULL;
560 uint32_t cUsersInList = 0;
561
562#ifdef RT_OS_WINDOWS
563 rc = VGSvcVMInfoWinWriteUsers(&g_VMInfoPropCache, &pszUserList, &cUsersInList);
564
565#elif defined(RT_OS_FREEBSD)
566 /** @todo FreeBSD: Port logged on user info retrieval.
567 * However, FreeBSD 9 supports utmpx, so we could use the code
568 * block below (?). */
569 rc = VERR_NOT_IMPLEMENTED;
570
571#elif defined(RT_OS_HAIKU)
572 /** @todo Haiku: Port logged on user info retrieval. */
573 rc = VERR_NOT_IMPLEMENTED;
574
575#elif defined(RT_OS_OS2)
576 /** @todo OS/2: Port logged on (LAN/local/whatever) user info retrieval. */
577 rc = VERR_NOT_IMPLEMENTED;
578
579#else
580 setutxent();
581 utmpx *ut_user;
582 uint32_t cListSize = 32;
583
584 /* Allocate a first array to hold 32 users max. */
585 char **papszUsers = (char **)RTMemAllocZ(cListSize * sizeof(char *));
586 if (papszUsers)
587 rc = VINF_SUCCESS;
588 else
589 rc = VERR_NO_MEMORY;
590
591 /* Process all entries in the utmp file.
592 * Note: This only handles */
593 while ( (ut_user = getutxent())
594 && RT_SUCCESS(rc))
595 {
596# ifdef RT_OS_DARWIN /* No ut_user->ut_session on Darwin */
597 VGSvcVerbose(4, "Found entry '%s' (type: %d, PID: %RU32)\n", ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid);
598# else
599 VGSvcVerbose(4, "Found entry '%s' (type: %d, PID: %RU32, session: %RU32)\n",
600 ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid, ut_user->ut_session);
601# endif
602 if (cUsersInList > cListSize)
603 {
604 cListSize += 32;
605 void *pvNew = RTMemRealloc(papszUsers, cListSize * sizeof(char*));
606 AssertBreakStmt(pvNew, cListSize -= 32);
607 papszUsers = (char **)pvNew;
608 }
609
610 /* Make sure we don't add user names which are not
611 * part of type USER_PROCES. */
612 if (ut_user->ut_type == USER_PROCESS) /* Regular user process. */
613 {
614 bool fFound = false;
615 for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
616 fFound = strncmp(papszUsers[i], ut_user->ut_user, sizeof(ut_user->ut_user)) == 0;
617
618 if (!fFound)
619 {
620 VGSvcVerbose(4, "Adding user '%s' (type: %d) to list\n", ut_user->ut_user, ut_user->ut_type);
621
622 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ut_user->ut_user);
623 if (RT_FAILURE(rc))
624 break;
625 cUsersInList++;
626 }
627 }
628 }
629
630# ifdef VBOX_WITH_DBUS
631# if defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
632 DBusError dbErr;
633 DBusConnection *pConnection = NULL;
634 int rc2 = RTDBusLoadLib();
635 bool fHaveLibDbus = false;
636 if (RT_SUCCESS(rc2))
637 {
638 /* Handle desktop sessions using ConsoleKit. */
639 VGSvcVerbose(4, "Checking ConsoleKit sessions ...\n");
640 fHaveLibDbus = true;
641 dbus_error_init(&dbErr);
642 pConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbErr);
643 }
644
645 if ( pConnection
646 && !dbus_error_is_set(&dbErr))
647 {
648 /* Get all available sessions. */
649/** @todo r=bird: What's the point of hardcoding things here when we've taken the pain of defining CK_XXX constants at the top of the file (or vice versa)? */
650 DBusMessage *pMsgSessions = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
651 "/org/freedesktop/ConsoleKit/Manager",
652 "org.freedesktop.ConsoleKit.Manager",
653 "GetSessions");
654 if ( pMsgSessions
655 && dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL)
656 {
657 DBusMessage *pReplySessions = dbus_connection_send_with_reply_and_block(pConnection,
658 pMsgSessions, 30 * 1000 /* 30s timeout */,
659 &dbErr);
660 if ( pReplySessions
661 && !dbus_error_is_set(&dbErr))
662 {
663 char **ppszSessions;
664 int cSessions;
665 if ( dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL
666 && vboxService_dbus_message_get_args(pReplySessions, &dbErr, DBUS_TYPE_ARRAY,
667 DBUS_TYPE_OBJECT_PATH, &ppszSessions, &cSessions,
668 DBUS_TYPE_INVALID /* Termination */))
669 {
670 VGSvcVerbose(4, "ConsoleKit: retrieved %RU16 session(s)\n", cSessions);
671
672 char **ppszCurSession = ppszSessions;
673 for (ppszCurSession; ppszCurSession && *ppszCurSession; ppszCurSession++)
674 {
675 VGSvcVerbose(4, "ConsoleKit: processing session '%s' ...\n", *ppszCurSession);
676
677 /* Only respect active sessions .*/
678 bool fActive = false;
679 DBusMessage *pMsgSessionActive = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
680 *ppszCurSession,
681 "org.freedesktop.ConsoleKit.Session",
682 "IsActive");
683 if ( pMsgSessionActive
684 && dbus_message_get_type(pMsgSessionActive) == DBUS_MESSAGE_TYPE_METHOD_CALL)
685 {
686 DBusMessage *pReplySessionActive = dbus_connection_send_with_reply_and_block(pConnection,
687 pMsgSessionActive,
688 30 * 1000 /*sec*/,
689 &dbErr);
690 if ( pReplySessionActive
691 && !dbus_error_is_set(&dbErr))
692 {
693 DBusMessageIter itMsg;
694 if ( dbus_message_iter_init(pReplySessionActive, &itMsg)
695 && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_BOOLEAN)
696 {
697 /* Get uid from message. */
698 int val;
699 dbus_message_iter_get_basic(&itMsg, &val);
700 fActive = val >= 1;
701 }
702
703 if (pReplySessionActive)
704 dbus_message_unref(pReplySessionActive);
705 }
706
707 if (pMsgSessionActive)
708 dbus_message_unref(pMsgSessionActive);
709 }
710
711 VGSvcVerbose(4, "ConsoleKit: session '%s' is %s\n",
712 *ppszCurSession, fActive ? "active" : "not active");
713
714 /* *ppszCurSession now contains the object path
715 * (e.g. "/org/freedesktop/ConsoleKit/Session1"). */
716 DBusMessage *pMsgUnixUser = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
717 *ppszCurSession,
718 "org.freedesktop.ConsoleKit.Session",
719 "GetUnixUser");
720 if ( fActive
721 && pMsgUnixUser
722 && dbus_message_get_type(pMsgUnixUser) == DBUS_MESSAGE_TYPE_METHOD_CALL)
723 {
724 DBusMessage *pReplyUnixUser = dbus_connection_send_with_reply_and_block(pConnection,
725 pMsgUnixUser,
726 30 * 1000 /* 30s timeout */,
727 &dbErr);
728 if ( pReplyUnixUser
729 && !dbus_error_is_set(&dbErr))
730 {
731 DBusMessageIter itMsg;
732 if ( dbus_message_iter_init(pReplyUnixUser, &itMsg)
733 && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_UINT32)
734 {
735 /* Get uid from message. */
736 uint32_t uid;
737 dbus_message_iter_get_basic(&itMsg, &uid);
738
739 /** @todo Add support for getting UID_MIN (/etc/login.defs on
740 * Debian). */
741 uint32_t uid_min = 1000;
742
743 /* Look up user name (realname) from uid. */
744 setpwent();
745 struct passwd *ppwEntry = getpwuid(uid);
746 if ( ppwEntry
747 && ppwEntry->pw_name)
748 {
749 if (ppwEntry->pw_uid >= uid_min /* Only respect users, not daemons etc. */)
750 {
751 VGSvcVerbose(4, "ConsoleKit: session '%s' -> %s (uid: %RU32)\n",
752 *ppszCurSession, ppwEntry->pw_name, uid);
753
754 bool fFound = false;
755 for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
756 fFound = strcmp(papszUsers[i], ppwEntry->pw_name) == 0;
757
758 if (!fFound)
759 {
760 VGSvcVerbose(4, "ConsoleKit: adding user '%s' to list\n", ppwEntry->pw_name);
761
762 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ppwEntry->pw_name);
763 if (RT_FAILURE(rc))
764 break;
765 cUsersInList++;
766 }
767 }
768 /* else silently ignore the user */
769 }
770 else
771 VGSvcError("ConsoleKit: unable to lookup user name for uid=%RU32\n", uid);
772 }
773 else
774 AssertMsgFailed(("ConsoleKit: GetUnixUser returned a wrong argument type\n"));
775 }
776
777 if (pReplyUnixUser)
778 dbus_message_unref(pReplyUnixUser);
779 }
780 else if (fActive) /* don't bitch about inactive users */
781 {
782 static int s_iBitchedAboutConsoleKit = 0;
783 if (s_iBitchedAboutConsoleKit < 1)
784 {
785 s_iBitchedAboutConsoleKit++;
786 VGSvcError("ConsoleKit: unable to retrieve user for session '%s' (msg type=%d): %s\n",
787 *ppszCurSession, dbus_message_get_type(pMsgUnixUser),
788 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
789 }
790 }
791
792 if (pMsgUnixUser)
793 dbus_message_unref(pMsgUnixUser);
794 }
795
796 dbus_free_string_array(ppszSessions);
797 }
798 else
799 VGSvcError("ConsoleKit: unable to retrieve session parameters (msg type=%d): %s\n",
800 dbus_message_get_type(pMsgSessions),
801 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
802 dbus_message_unref(pReplySessions);
803 }
804
805 if (pMsgSessions)
806 {
807 dbus_message_unref(pMsgSessions);
808 pMsgSessions = NULL;
809 }
810 }
811 else
812 {
813 static int s_iBitchedAboutConsoleKit = 0;
814 if (s_iBitchedAboutConsoleKit < 3)
815 {
816 s_iBitchedAboutConsoleKit++;
817 VGSvcError("Unable to invoke ConsoleKit (%d/3) -- maybe not installed / used? Error: %s\n",
818 s_iBitchedAboutConsoleKit,
819 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
820 }
821 }
822
823 if (pMsgSessions)
824 dbus_message_unref(pMsgSessions);
825 }
826 else
827 {
828 static int s_iBitchedAboutDBus = 0;
829 if (s_iBitchedAboutDBus < 3)
830 {
831 s_iBitchedAboutDBus++;
832 VGSvcError("Unable to connect to system D-Bus (%d/3): %s\n", s_iBitchedAboutDBus,
833 fHaveLibDbus && dbus_error_is_set(&dbErr) ? dbErr.message : "D-Bus not installed");
834 }
835 }
836
837 if ( fHaveLibDbus
838 && dbus_error_is_set(&dbErr))
839 dbus_error_free(&dbErr);
840# endif /* RT_OS_LINUX */
841# endif /* VBOX_WITH_DBUS */
842
843 /** @todo Fedora/others: Handle systemd-loginctl. */
844
845 /* Calc the string length. */
846 size_t cchUserList = 0;
847 if (RT_SUCCESS(rc))
848 for (uint32_t i = 0; i < cUsersInList; i++)
849 cchUserList += (i != 0) + strlen(papszUsers[i]);
850
851 /* Build the user list. */
852 if (cchUserList > 0)
853 {
854 if (RT_SUCCESS(rc))
855 rc = RTStrAllocEx(&pszUserList, cchUserList + 1);
856 if (RT_SUCCESS(rc))
857 {
858 char *psz = pszUserList;
859 for (uint32_t i = 0; i < cUsersInList; i++)
860 {
861 if (i != 0)
862 *psz++ = ',';
863 size_t cch = strlen(papszUsers[i]);
864 memcpy(psz, papszUsers[i], cch);
865 psz += cch;
866 }
867 *psz = '\0';
868 }
869 }
870
871 /* Cleanup. */
872 for (uint32_t i = 0; i < cUsersInList; i++)
873 RTStrFree(papszUsers[i]);
874 RTMemFree(papszUsers);
875
876 endutxent(); /* Close utmpx file. */
877#endif /* !RT_OS_WINDOWS && !RT_OS_FREEBSD && !RT_OS_HAIKU && !RT_OS_OS2 */
878
879 Assert(RT_FAILURE(rc) || cUsersInList == 0 || (pszUserList && *pszUserList));
880
881 /*
882 * If the user enumeration above failed, reset the user count to 0 except
883 * we didn't have enough memory anymore. In that case we want to preserve
884 * the previous user count in order to not confuse third party tools which
885 * rely on that count.
886 */
887 if (RT_FAILURE(rc))
888 {
889 if (rc == VERR_NO_MEMORY)
890 {
891 static int s_iVMInfoBitchedOOM = 0;
892 if (s_iVMInfoBitchedOOM++ < 3)
893 VGSvcVerbose(0, "Warning: Not enough memory available to enumerate users! Keeping old value (%RU32)\n",
894 g_cVMInfoLoggedInUsers);
895 cUsersInList = g_cVMInfoLoggedInUsers;
896 }
897 else
898 cUsersInList = 0;
899 }
900 else /* Preserve logged in users count. */
901 g_cVMInfoLoggedInUsers = cUsersInList;
902
903 VGSvcVerbose(4, "cUsersInList=%RU32, pszUserList=%s, rc=%Rrc\n", cUsersInList, pszUserList ? pszUserList : "<NULL>", rc);
904
905 if (pszUserList)
906 {
907 AssertMsg(cUsersInList, ("pszUserList contains users whereas cUsersInList is 0\n"));
908 rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, "%s", pszUserList);
909 }
910 else
911 rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, NULL);
912 if (RT_FAILURE(rc))
913 VGSvcError("Error writing logged in users list, rc=%Rrc\n", rc);
914
915 rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers, "%RU32", cUsersInList);
916 if (RT_FAILURE(rc))
917 VGSvcError("Error writing logged in users count, rc=%Rrc\n", rc);
918
919/** @todo r=bird: What's this 'beacon' nonsense here? It's _not_ defined with
920 * the VGSVCPROPCACHE_FLAGS_ALWAYS_UPDATE flag set!! */
921 rc = VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers, cUsersInList == 0 ? "true" : "false");
922 if (RT_FAILURE(rc))
923 VGSvcError("Error writing no logged in users beacon, rc=%Rrc\n", rc);
924
925 if (pszUserList)
926 RTStrFree(pszUserList);
927
928 VGSvcVerbose(4, "Writing users returned with rc=%Rrc\n", rc);
929 return rc;
930}
931
932
933/**
934 * Provide information about the guest network.
935 */
936static int vgsvcVMInfoWriteNetwork(void)
937{
938 uint32_t cIfsReported = 0;
939 char szPropPath[256];
940
941#ifdef RT_OS_WINDOWS
942 /*
943 * Check that the APIs we need are present.
944 */
945 if ( !g_pfnWSAIoctl
946 || !g_pfnWSASocketA
947 || !g_pfnWSAGetLastError
948 || !g_pfninet_ntoa
949 || !g_pfnclosesocket)
950 return VINF_SUCCESS;
951
952 /*
953 * Query the IP adapter info first, if we have the API.
954 */
955 IP_ADAPTER_INFO *pAdpInfo = NULL;
956 if (g_pfnGetAdaptersInfo)
957 {
958 ULONG cbAdpInfo = RT_MAX(sizeof(IP_ADAPTER_INFO) * 2, _2K);
959 pAdpInfo = (IP_ADAPTER_INFO *)RTMemAllocZ(cbAdpInfo);
960 if (!pAdpInfo)
961 {
962 VGSvcError("VMInfo/Network: Failed to allocate two IP_ADAPTER_INFO structures\n");
963 return VERR_NO_MEMORY;
964 }
965
966 DWORD dwRet = g_pfnGetAdaptersInfo(pAdpInfo, &cbAdpInfo);
967 if (dwRet == ERROR_BUFFER_OVERFLOW)
968 {
969 IP_ADAPTER_INFO *pAdpInfoNew = (IP_ADAPTER_INFO*)RTMemRealloc(pAdpInfo, cbAdpInfo);
970 if (pAdpInfoNew)
971 {
972 pAdpInfo = pAdpInfoNew;
973 RT_BZERO(pAdpInfo, cbAdpInfo);
974 dwRet = g_pfnGetAdaptersInfo(pAdpInfo, &cbAdpInfo);
975 }
976 }
977 if (dwRet != NO_ERROR)
978 {
979 RTMemFree(pAdpInfo);
980 pAdpInfo = NULL;
981 if (dwRet == ERROR_NO_DATA)
982 /* If no network adapters available / present in the
983 system we pretend success to not bail out too early. */
984 VGSvcVerbose(3, "VMInfo/Network: No network adapters present according to GetAdaptersInfo.\n");
985 else
986 {
987 VGSvcError("VMInfo/Network: Failed to get adapter info: Error %d\n", dwRet);
988 return RTErrConvertFromWin32(dwRet);
989 }
990 }
991 }
992
993 /*
994 * Ask the TCP/IP stack for an interface list.
995 */
996 SOCKET sd = g_pfnWSASocketA(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
997 if (sd == SOCKET_ERROR) /* Socket invalid. */
998 {
999 int const wsaErr = g_pfnWSAGetLastError();
1000 RTMemFree(pAdpInfo);
1001
1002 /* Don't complain/bail out with an error if network stack is not up; can happen
1003 * on NT4 due to start up when not connected shares dialogs pop up. */
1004 if (wsaErr == WSAENETDOWN)
1005 {
1006 VGSvcVerbose(0, "VMInfo/Network: Network is not up yet.\n");
1007 return VINF_SUCCESS;
1008 }
1009 VGSvcError("VMInfo/Network: Failed to get a socket: Error %d\n", wsaErr);
1010 return RTErrConvertFromWin32(wsaErr);
1011 }
1012
1013 INTERFACE_INFO aInterfaces[20] = {{0}};
1014 DWORD cbReturned = 0;
1015# ifdef RT_ARCH_X86
1016 /* Workaround for uninitialized variable used in memcpy in GetTcpipInterfaceList
1017 (NT4SP1 at least). It seems to be happy enough with garbages, no failure
1018 returns so far, so we just need to prevent it from crashing by filling the
1019 stack with valid pointer values prior to the API call. */
1020 _asm
1021 {
1022 mov edx, edi
1023 lea eax, aInterfaces
1024 mov [esp - 0x1000], eax
1025 mov [esp - 0x2000], eax
1026 mov ecx, 0x2000/4 - 1
1027 cld
1028 lea edi, [esp - 0x2000]
1029 rep stosd
1030 mov edi, edx
1031 }
1032# endif
1033 int rc = g_pfnWSAIoctl(sd,
1034 SIO_GET_INTERFACE_LIST,
1035 NULL, /* pvInBuffer */
1036 0, /* cbInBuffer */
1037 &aInterfaces[0], /* pvOutBuffer */
1038 sizeof(aInterfaces), /* cbOutBuffer */
1039 &cbReturned,
1040 NULL, /* pOverlapped */
1041 NULL); /* pCompletionRoutine */
1042 if (rc == SOCKET_ERROR)
1043 {
1044 VGSvcError("VMInfo/Network: Failed to WSAIoctl() on socket: Error: %d\n", g_pfnWSAGetLastError());
1045 RTMemFree(pAdpInfo);
1046 g_pfnclosesocket(sd);
1047 return RTErrConvertFromWin32(g_pfnWSAGetLastError());
1048 }
1049 g_pfnclosesocket(sd);
1050 int cIfacesSystem = cbReturned / sizeof(INTERFACE_INFO);
1051
1052 /*
1053 * Iterate the inteface list we got back from the TCP/IP,
1054 * using the pAdpInfo list to supply the MAC address.
1055 */
1056 /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
1057 for (int i = 0; i < cIfacesSystem; ++i)
1058 {
1059 if (aInterfaces[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
1060 continue;
1061 sockaddr_in *pAddress = &aInterfaces[i].iiAddress.AddressIn;
1062 char szIp[32];
1063 RTStrPrintf(szIp, sizeof(szIp), "%s", g_pfninet_ntoa(pAddress->sin_addr));
1064 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfsReported);
1065 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szIp);
1066
1067 pAddress = &aInterfaces[i].iiBroadcastAddress.AddressIn;
1068 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfsReported);
1069 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", g_pfninet_ntoa(pAddress->sin_addr));
1070
1071 pAddress = (sockaddr_in *)&aInterfaces[i].iiNetmask;
1072 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfsReported);
1073 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", g_pfninet_ntoa(pAddress->sin_addr));
1074
1075 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfsReported);
1076 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, aInterfaces[i].iiFlags & IFF_UP ? "Up" : "Down");
1077
1078 if (pAdpInfo)
1079 {
1080 IP_ADAPTER_INFO *pAdp;
1081 for (pAdp = pAdpInfo; pAdp; pAdp = pAdp->Next)
1082 if (!strcmp(pAdp->IpAddressList.IpAddress.String, szIp))
1083 break;
1084
1085 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfsReported);
1086 if (pAdp)
1087 {
1088 char szMac[32];
1089 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
1090 pAdp->Address[0], pAdp->Address[1], pAdp->Address[2],
1091 pAdp->Address[3], pAdp->Address[4], pAdp->Address[5]);
1092 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
1093 }
1094 else
1095 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, NULL);
1096 }
1097
1098 cIfsReported++;
1099 }
1100
1101 RTMemFree(pAdpInfo);
1102
1103#elif defined(RT_OS_HAIKU)
1104 /** @todo Haiku: implement network info. retreival */
1105 return VERR_NOT_IMPLEMENTED;
1106
1107#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD)
1108 struct ifaddrs *pIfHead = NULL;
1109
1110 /* Get all available interfaces */
1111 int rc = getifaddrs(&pIfHead);
1112 if (rc < 0)
1113 {
1114 rc = RTErrConvertFromErrno(errno);
1115 VGSvcError("VMInfo/Network: Failed to get all interfaces: Error %Rrc\n");
1116 return rc;
1117 }
1118
1119 /* Loop through all interfaces and set the data. */
1120 for (struct ifaddrs *pIfCurr = pIfHead; pIfCurr; pIfCurr = pIfCurr->ifa_next)
1121 {
1122 /*
1123 * Only AF_INET and no loopback interfaces
1124 */
1125 /** @todo IPv6 interfaces */
1126 if ( pIfCurr->ifa_addr->sa_family == AF_INET
1127 && !(pIfCurr->ifa_flags & IFF_LOOPBACK))
1128 {
1129 char szInetAddr[NI_MAXHOST];
1130
1131 memset(szInetAddr, 0, NI_MAXHOST);
1132 getnameinfo(pIfCurr->ifa_addr, sizeof(struct sockaddr_in),
1133 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1134 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfsReported);
1135 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1136
1137 memset(szInetAddr, 0, NI_MAXHOST);
1138 getnameinfo(pIfCurr->ifa_broadaddr, sizeof(struct sockaddr_in),
1139 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1140 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfsReported);
1141 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1142
1143 memset(szInetAddr, 0, NI_MAXHOST);
1144 getnameinfo(pIfCurr->ifa_netmask, sizeof(struct sockaddr_in),
1145 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1146 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfsReported);
1147 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1148
1149 /* Search for the AF_LINK interface of the current AF_INET one and get the mac. */
1150 for (struct ifaddrs *pIfLinkCurr = pIfHead; pIfLinkCurr; pIfLinkCurr = pIfLinkCurr->ifa_next)
1151 {
1152 if ( pIfLinkCurr->ifa_addr->sa_family == AF_LINK
1153 && !strcmp(pIfCurr->ifa_name, pIfLinkCurr->ifa_name))
1154 {
1155 char szMac[32];
1156 uint8_t *pu8Mac = NULL;
1157 struct sockaddr_dl *pLinkAddress = (struct sockaddr_dl *)pIfLinkCurr->ifa_addr;
1158
1159 AssertPtr(pLinkAddress);
1160 pu8Mac = (uint8_t *)LLADDR(pLinkAddress);
1161 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
1162 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
1163 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfsReported);
1164 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
1165 break;
1166 }
1167 }
1168
1169 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfsReported);
1170 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down");
1171
1172 cIfsReported++;
1173 }
1174 }
1175
1176 /* Free allocated resources. */
1177 freeifaddrs(pIfHead);
1178
1179#else /* !RT_OS_WINDOWS && !RT_OS_FREEBSD */
1180 /*
1181 * Use SIOCGIFCONF to get a list of interface/protocol configurations.
1182 *
1183 * See "UNIX Network Programming Volume 1" by W. R. Stevens, section 17.6
1184 * for details on this ioctl.
1185 */
1186 int sd = socket(AF_INET, SOCK_DGRAM, 0);
1187 if (sd < 0)
1188 {
1189 int rc = RTErrConvertFromErrno(errno);
1190 VGSvcError("VMInfo/Network: Failed to get a socket: Error %Rrc\n", rc);
1191 return rc;
1192 }
1193
1194 /* Call SIOCGIFCONF with the right sized buffer (remember the size). */
1195 static int s_cbBuf = 256; // 1024
1196 int cbBuf = s_cbBuf;
1197 char *pchBuf;
1198 struct ifconf IfConf;
1199 int rc = VINF_SUCCESS;
1200 for (;;)
1201 {
1202 pchBuf = (char *)RTMemTmpAllocZ(cbBuf);
1203 if (!pchBuf)
1204 {
1205 rc = VERR_NO_TMP_MEMORY;
1206 break;
1207 }
1208
1209 IfConf.ifc_len = cbBuf;
1210 IfConf.ifc_buf = pchBuf;
1211 if (ioctl(sd, SIOCGIFCONF, &IfConf) >= 0)
1212 {
1213 /* Hard to anticipate how space an address might possibly take, so
1214 making some generous assumptions here to avoid performing the
1215 query twice with different buffer sizes. */
1216 if (IfConf.ifc_len + 128 < cbBuf)
1217 break;
1218 }
1219 else if (errno != EOVERFLOW)
1220 {
1221 rc = RTErrConvertFromErrno(errno);
1222 break;
1223 }
1224
1225 /* grow the buffer */
1226 s_cbBuf = cbBuf *= 2;
1227 RTMemFree(pchBuf);
1228 }
1229 if (RT_FAILURE(rc))
1230 {
1231 close(sd);
1232 RTMemTmpFree(pchBuf);
1233 VGSvcError("VMInfo/Network: Error doing SIOCGIFCONF (cbBuf=%d): %Rrc\n", cbBuf, rc);
1234 return rc;
1235 }
1236
1237 /*
1238 * Iterate the interface/protocol configurations.
1239 *
1240 * Note! The current code naively assumes one IPv4 address per interface.
1241 * This means that guest assigning more than one address to an
1242 * interface will get multiple entries for one physical interface.
1243 */
1244# ifdef RT_OS_OS2
1245 struct ifreq *pPrevLinkAddr = NULL;
1246# endif
1247 struct ifreq *pCur = IfConf.ifc_req;
1248 size_t cbLeft = IfConf.ifc_len;
1249 while (cbLeft >= sizeof(*pCur))
1250 {
1251# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
1252 /* These two do not provide the sa_len member but only support address
1253 * families which do not need extra bytes on the end. */
1254# define SA_LEN(pAddr) sizeof(struct sockaddr)
1255# elif !defined(SA_LEN)
1256# define SA_LEN(pAddr) (pAddr)->sa_len
1257# endif
1258 /* Figure the size of the current request. */
1259 size_t cbCur = RT_UOFFSETOF(struct ifreq, ifr_addr)
1260 + SA_LEN(&pCur->ifr_addr);
1261 cbCur = RT_MAX(cbCur, sizeof(struct ifreq));
1262# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
1263 Assert(pCur->ifr_addr.sa_family == AF_INET);
1264# endif
1265 AssertBreak(cbCur <= cbLeft);
1266
1267# ifdef RT_OS_OS2
1268 /* On OS/2 we get the MAC address in the AF_LINK that the BSD 4.4 stack
1269 emits. We boldly ASSUME these always comes first. */
1270 if ( pCur->ifr_addr.sa_family == AF_LINK
1271 && ((struct sockaddr_dl *)&pCur->ifr_addr)->sdl_alen == 6)
1272 pPrevLinkAddr = pCur;
1273# endif
1274
1275 /* Skip it if it's not the kind of address we're looking for. */
1276 struct ifreq IfReqTmp;
1277 bool fIfUp = false;
1278 bool fSkip = false;
1279 if (pCur->ifr_addr.sa_family != AF_INET)
1280 fSkip = true;
1281 else
1282 {
1283 /* Get the interface flags so we can detect loopback and check if it's up. */
1284 IfReqTmp = *pCur;
1285 if (ioctl(sd, SIOCGIFFLAGS, &IfReqTmp) < 0)
1286 {
1287 rc = RTErrConvertFromErrno(errno);
1288 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFFLAGS,%s) on socket: Error %Rrc\n", pCur->ifr_name, rc);
1289 break;
1290 }
1291 fIfUp = !!(IfReqTmp.ifr_flags & IFF_UP);
1292 if (IfReqTmp.ifr_flags & IFF_LOOPBACK) /* Skip the loopback device. */
1293 fSkip = true;
1294 }
1295 if (!fSkip)
1296 {
1297 size_t offSubProp = RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32", cIfsReported);
1298
1299 sockaddr_in *pAddress = (sockaddr_in *)&pCur->ifr_addr;
1300 strcpy(&szPropPath[offSubProp], "/V4/IP");
1301 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1302
1303 /* Get the broadcast address. */
1304 IfReqTmp = *pCur;
1305 if (ioctl(sd, SIOCGIFBRDADDR, &IfReqTmp) < 0)
1306 {
1307 rc = RTErrConvertFromErrno(errno);
1308 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %Rrc\n", rc);
1309 break;
1310 }
1311 pAddress = (sockaddr_in *)&IfReqTmp.ifr_broadaddr;
1312 strcpy(&szPropPath[offSubProp], "/V4/Broadcast");
1313 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1314
1315 /* Get the net mask. */
1316 IfReqTmp = *pCur;
1317 if (ioctl(sd, SIOCGIFNETMASK, &IfReqTmp) < 0)
1318 {
1319 rc = RTErrConvertFromErrno(errno);
1320 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFNETMASK) on socket: Error %Rrc\n", rc);
1321 break;
1322 }
1323# if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
1324 pAddress = (sockaddr_in *)&IfReqTmp.ifr_addr;
1325# else
1326 pAddress = (sockaddr_in *)&IfReqTmp.ifr_netmask;
1327# endif
1328 strcpy(&szPropPath[offSubProp], "/V4/Netmask");
1329 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1330
1331# if defined(RT_OS_SOLARIS)
1332 /*
1333 * "ifreq" is obsolete on Solaris. We use the recommended "lifreq".
1334 * We might fail if the interface has not been assigned an IP address.
1335 * That doesn't matter; as long as it's plumbed we can pick it up.
1336 * But, if it has not acquired an IP address we cannot obtain it's MAC
1337 * address this way, so we just use all zeros there.
1338 */
1339 RTMAC IfMac;
1340 struct lifreq IfReq;
1341 RT_ZERO(IfReq);
1342 AssertCompile(sizeof(IfReq.lifr_name) >= sizeof(pCur->ifr_name));
1343 strncpy(IfReq.lifr_name, pCur->ifr_name, sizeof(IfReq.lifr_name));
1344 if (ioctl(sd, SIOCGLIFADDR, &IfReq) >= 0)
1345 {
1346 struct arpreq ArpReq;
1347 RT_ZERO(ArpReq);
1348 memcpy(&ArpReq.arp_pa, &IfReq.lifr_addr, sizeof(struct sockaddr_in));
1349
1350 if (ioctl(sd, SIOCGARP, &ArpReq) >= 0)
1351 memcpy(&IfMac, ArpReq.arp_ha.sa_data, sizeof(IfMac));
1352 else
1353 {
1354 rc = RTErrConvertFromErrno(errno);
1355 VGSvcError("VMInfo/Network: failed to ioctl(SIOCGARP) on socket: Error %Rrc\n", rc);
1356 break;
1357 }
1358 }
1359 else
1360 {
1361 VGSvcVerbose(2, "VMInfo/Network: Interface '%s' has no assigned IP address, skipping ...\n", pCur->ifr_name);
1362 continue;
1363 }
1364# elif defined(RT_OS_OS2)
1365 RTMAC IfMac;
1366 if ( pPrevLinkAddr
1367 && strncmp(pCur->ifr_name, pPrevLinkAddr->ifr_name, sizeof(pCur->ifr_name)) == 0)
1368 {
1369 struct sockaddr_dl *pDlAddr = (struct sockaddr_dl *)&pPrevLinkAddr->ifr_addr;
1370 IfMac = *(PRTMAC)&pDlAddr->sdl_data[pDlAddr->sdl_nlen];
1371 }
1372 else
1373 RT_ZERO(IfMac);
1374#else
1375 if (ioctl(sd, SIOCGIFHWADDR, &IfReqTmp) < 0)
1376 {
1377 rc = RTErrConvertFromErrno(errno);
1378 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFHWADDR) on socket: Error %Rrc\n", rc);
1379 break;
1380 }
1381 RTMAC IfMac = *(PRTMAC)&IfReqTmp.ifr_hwaddr.sa_data[0];
1382# endif
1383 strcpy(&szPropPath[offSubProp], "/MAC");
1384 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%02X%02X%02X%02X%02X%02X",
1385 IfMac.au8[0], IfMac.au8[1], IfMac.au8[2], IfMac.au8[3], IfMac.au8[4], IfMac.au8[5]);
1386
1387 strcpy(&szPropPath[offSubProp], "/Status");
1388 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, fIfUp ? "Up" : "Down");
1389
1390 /* The name. */
1391 int rc2 = RTStrValidateEncodingEx(pCur->ifr_name, sizeof(pCur->ifr_name), 0);
1392 if (RT_SUCCESS(rc2))
1393 {
1394 strcpy(&szPropPath[offSubProp], "/Name");
1395 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%.*s", sizeof(pCur->ifr_name), pCur->ifr_name);
1396 }
1397
1398 cIfsReported++;
1399 }
1400
1401 /*
1402 * Next interface/protocol configuration.
1403 */
1404 pCur = (struct ifreq *)((uintptr_t)pCur + cbCur);
1405 cbLeft -= cbCur;
1406 }
1407
1408 RTMemTmpFree(pchBuf);
1409 close(sd);
1410 if (RT_FAILURE(rc))
1411 VGSvcError("VMInfo/Network: Network enumeration for interface %RU32 failed with error %Rrc\n", cIfsReported, rc);
1412
1413#endif /* !RT_OS_WINDOWS */
1414
1415#if 0 /* Zapping not enabled yet, needs more testing first. */
1416 /*
1417 * Zap all stale network interface data if the former (saved) network ifaces count
1418 * is bigger than the current one.
1419 */
1420
1421 /* Get former count. */
1422 uint32_t cIfsReportedOld;
1423 rc = VGSvcReadPropUInt32(g_uVMInfoGuestPropSvcClientID, g_pszPropCacheValNetCount, &cIfsReportedOld,
1424 0 /* Min */, UINT32_MAX /* Max */);
1425 if ( RT_SUCCESS(rc)
1426 && cIfsReportedOld > cIfsReported) /* Are some ifaces not around anymore? */
1427 {
1428 VGSvcVerbose(3, "VMInfo/Network: Stale interface data detected (%RU32 old vs. %RU32 current)\n",
1429 cIfsReportedOld, cIfsReported);
1430
1431 uint32_t uIfaceDeleteIdx = cIfsReported;
1432 do
1433 {
1434 VGSvcVerbose(3, "VMInfo/Network: Deleting stale data of interface %d ...\n", uIfaceDeleteIdx);
1435 rc = VGSvcPropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%RU32", uIfaceDeleteIdx++);
1436 } while (RT_SUCCESS(rc));
1437 }
1438 else if ( RT_FAILURE(rc)
1439 && rc != VERR_NOT_FOUND)
1440 {
1441 VGSvcError("VMInfo/Network: Failed retrieving old network interfaces count with error %Rrc\n", rc);
1442 }
1443#endif
1444
1445 /*
1446 * This property is a beacon which is _always_ written, even if the network configuration
1447 * does not change. If this property is missing, the host assumes that all other GuestInfo
1448 * properties are no longer valid.
1449 */
1450 VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNetCount, "%RU32", cIfsReported);
1451
1452 /* Don't fail here; just report everything we got. */
1453 return VINF_SUCCESS;
1454}
1455
1456
1457/**
1458 * @interface_method_impl{VBOXSERVICE,pfnWorker}
1459 */
1460static DECLCALLBACK(int) vbsvcVMInfoWorker(bool volatile *pfShutdown)
1461{
1462 int rc;
1463
1464 /*
1465 * Tell the control thread that it can continue
1466 * spawning services.
1467 */
1468 RTThreadUserSignal(RTThreadSelf());
1469
1470#ifdef RT_OS_WINDOWS
1471 /* Required for network information (must be called per thread). */
1472 if (g_pfnWSAStartup)
1473 {
1474 WSADATA wsaData;
1475 RT_ZERO(wsaData);
1476 if (g_pfnWSAStartup(MAKEWORD(2, 2), &wsaData))
1477 VGSvcError("VMInfo/Network: WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(g_pfnWSAGetLastError()));
1478 }
1479#endif
1480
1481 /*
1482 * Write the fixed properties first.
1483 */
1484 vgsvcVMInfoWriteFixedProperties();
1485
1486 /*
1487 * Now enter the loop retrieving runtime data continuously.
1488 */
1489 for (;;)
1490 {
1491 rc = vgsvcVMInfoWriteUsers();
1492 if (RT_FAILURE(rc))
1493 break;
1494
1495 rc = vgsvcVMInfoWriteNetwork();
1496 if (RT_FAILURE(rc))
1497 break;
1498
1499 /* Whether to wait for event semaphore or not. */
1500 bool fWait = true;
1501
1502 /* Check for location awareness. This most likely only
1503 * works with VBox (latest) 4.1 and up. */
1504
1505 /* Check for new connection. */
1506 char *pszLAClientID = NULL;
1507 int rc2 = VGSvcReadHostProp(g_uVMInfoGuestPropSvcClientID, g_pszLAActiveClient, true /* Read only */,
1508 &pszLAClientID, NULL /* Flags */, NULL /* Timestamp */);
1509 if (RT_SUCCESS(rc2))
1510 {
1511 AssertPtr(pszLAClientID);
1512 if (RTStrICmp(pszLAClientID, "0")) /* Is a client connected? */
1513 {
1514 uint32_t uLAClientID = RTStrToInt32(pszLAClientID);
1515 uint64_t uLAClientAttachedTS;
1516
1517 /* Peek at "Attach" value to figure out if hotdesking happened. */
1518 char *pszAttach = NULL;
1519 rc2 = vgsvcGetLAClientValue(uLAClientID, "Attach", &pszAttach,
1520 &uLAClientAttachedTS);
1521
1522 if ( RT_SUCCESS(rc2)
1523 && ( !g_LAClientAttachedTS
1524 || (g_LAClientAttachedTS != uLAClientAttachedTS)))
1525 {
1526 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1527
1528 /* Note: There is a race between setting the guest properties by the host and getting them by
1529 * the guest. */
1530 rc2 = vgsvcGetLAClientInfo(uLAClientID, &g_LAClientInfo);
1531 if (RT_SUCCESS(rc2))
1532 {
1533 VGSvcVerbose(1, "VRDP: Hotdesk client %s with ID=%RU32, Name=%s, Domain=%s\n",
1534 /* If g_LAClientAttachedTS is 0 this means there already was an active
1535 * hotdesk session when VBoxService started. */
1536 !g_LAClientAttachedTS ? "already active" : g_LAClientInfo.fAttached ? "connected" : "disconnected",
1537 uLAClientID, g_LAClientInfo.pszName, g_LAClientInfo.pszDomain);
1538
1539 g_LAClientAttachedTS = g_LAClientInfo.uAttachedTS;
1540
1541 /* Don't wait for event semaphore below anymore because we now know that the client
1542 * changed. This means we need to iterate all VM information again immediately. */
1543 fWait = false;
1544 }
1545 else
1546 {
1547 static int s_iBitchedAboutLAClientInfo = 0;
1548 if (s_iBitchedAboutLAClientInfo < 10)
1549 {
1550 s_iBitchedAboutLAClientInfo++;
1551 VGSvcError("Error getting active location awareness client info, rc=%Rrc\n", rc2);
1552 }
1553 }
1554 }
1555 else if (RT_FAILURE(rc2))
1556 VGSvcError("Error getting attached value of location awareness client %RU32, rc=%Rrc\n", uLAClientID, rc2);
1557 if (pszAttach)
1558 RTStrFree(pszAttach);
1559 }
1560 else
1561 {
1562 VGSvcVerbose(1, "VRDP: UTTSC disconnected from VRDP server\n");
1563 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1564 }
1565
1566 RTStrFree(pszLAClientID);
1567 }
1568 else
1569 {
1570 static int s_iBitchedAboutLAClient = 0;
1571 if ( (rc2 != VERR_NOT_FOUND) /* No location awareness installed, skip. */
1572 && s_iBitchedAboutLAClient < 3)
1573 {
1574 s_iBitchedAboutLAClient++;
1575 VGSvcError("VRDP: Querying connected location awareness client failed with rc=%Rrc\n", rc2);
1576 }
1577 }
1578
1579 VGSvcVerbose(3, "VRDP: Handling location awareness done\n");
1580
1581 /*
1582 * Flush all properties if we were restored.
1583 */
1584 uint64_t idNewSession = g_idVMInfoSession;
1585 VbglR3GetSessionId(&idNewSession);
1586 if (idNewSession != g_idVMInfoSession)
1587 {
1588 VGSvcVerbose(3, "The VM session ID changed, flushing all properties\n");
1589 vgsvcVMInfoWriteFixedProperties();
1590 VGSvcPropCacheFlush(&g_VMInfoPropCache);
1591 g_idVMInfoSession = idNewSession;
1592 }
1593
1594 /*
1595 * Block for a while.
1596 *
1597 * The event semaphore takes care of ignoring interruptions and it
1598 * allows us to implement service wakeup later.
1599 */
1600 if (*pfShutdown)
1601 break;
1602 if (fWait)
1603 rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval);
1604 if (*pfShutdown)
1605 break;
1606 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
1607 {
1608 VGSvcError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
1609 rc = rc2;
1610 break;
1611 }
1612 else if (RT_LIKELY(RT_SUCCESS(rc2)))
1613 {
1614 /* Reset event semaphore if it got triggered. */
1615 rc2 = RTSemEventMultiReset(g_hVMInfoEvent);
1616 if (RT_FAILURE(rc2))
1617 rc2 = VGSvcError("RTSemEventMultiReset failed; rc2=%Rrc\n", rc2);
1618 }
1619 }
1620
1621#ifdef RT_OS_WINDOWS
1622 if (g_pfnWSACleanup)
1623 g_pfnWSACleanup();
1624#endif
1625
1626 return rc;
1627}
1628
1629
1630/**
1631 * @interface_method_impl{VBOXSERVICE,pfnStop}
1632 */
1633static DECLCALLBACK(void) vbsvcVMInfoStop(void)
1634{
1635 RTSemEventMultiSignal(g_hVMInfoEvent);
1636}
1637
1638
1639/**
1640 * @interface_method_impl{VBOXSERVICE,pfnTerm}
1641 */
1642static DECLCALLBACK(void) vbsvcVMInfoTerm(void)
1643{
1644 if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
1645 {
1646 /** @todo temporary solution: Zap all values which are not valid
1647 * anymore when VM goes down (reboot/shutdown ). Needs to
1648 * be replaced with "temporary properties" later.
1649 *
1650 * One idea is to introduce a (HGCM-)session guest property
1651 * flag meaning that a guest property is only valid as long
1652 * as the HGCM session isn't closed (e.g. guest application
1653 * terminates). [don't remove till implemented]
1654 */
1655 /** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and use the cache
1656 * since it remembers what we've written. */
1657 /* Delete the "../Net" branch. */
1658 const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
1659 int rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
1660
1661 /* Destroy LA client info. */
1662 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1663
1664 /* Destroy property cache. */
1665 VGSvcPropCacheDestroy(&g_VMInfoPropCache);
1666
1667 /* Disconnect from guest properties service. */
1668 rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID);
1669 if (RT_FAILURE(rc))
1670 VGSvcError("Failed to disconnect from guest property service! Error: %Rrc\n", rc);
1671 g_uVMInfoGuestPropSvcClientID = 0;
1672
1673 RTSemEventMultiDestroy(g_hVMInfoEvent);
1674 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
1675 }
1676}
1677
1678
1679/**
1680 * The 'vminfo' service description.
1681 */
1682VBOXSERVICE g_VMInfo =
1683{
1684 /* pszName. */
1685 "vminfo",
1686 /* pszDescription. */
1687 "Virtual Machine Information",
1688 /* pszUsage. */
1689 " [--vminfo-interval <ms>] [--vminfo-user-idle-threshold <ms>]"
1690 ,
1691 /* pszOptions. */
1692 " --vminfo-interval Specifies the interval at which to retrieve the\n"
1693 " VM information. The default is 10000 ms.\n"
1694 " --vminfo-user-idle-threshold <ms>\n"
1695 " Specifies the user idle threshold (in ms) for\n"
1696 " considering a guest user as being idle. The default\n"
1697 " is 5000 (5 seconds).\n"
1698 ,
1699 /* methods */
1700 vbsvcVMInfoPreInit,
1701 vbsvcVMInfoOption,
1702 vbsvcVMInfoInit,
1703 vbsvcVMInfoWorker,
1704 vbsvcVMInfoStop,
1705 vbsvcVMInfoTerm
1706};
1707
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