VirtualBox

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

最後變更 在這個檔案從96503是 96407,由 vboxsync 提交於 3 年 前

scm copyright and license note update

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 66.9 KB
 
1/* $Id: VBoxServiceVMInfo.cpp 96407 2022-08-22 17:43:14Z 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.alldomusa.eu.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 if ( !g_pfnGetAdaptersInfo
947 && ( !g_pfnWSAIoctl
948 || !g_pfnWSASocketA
949 || !g_pfnWSAGetLastError
950 || !g_pfninet_ntoa
951 || !g_pfnclosesocket) )
952 return VINF_SUCCESS;
953
954 ULONG cbAdpInfo = sizeof(IP_ADAPTER_INFO);
955 IP_ADAPTER_INFO *pAdpInfo = (IP_ADAPTER_INFO *)RTMemAllocZ(cbAdpInfo);
956 if (!pAdpInfo)
957 {
958 VGSvcError("VMInfo/Network: Failed to allocate IP_ADAPTER_INFO\n");
959 return VERR_NO_MEMORY;
960 }
961 DWORD dwRet = g_pfnGetAdaptersInfo ? g_pfnGetAdaptersInfo(pAdpInfo, &cbAdpInfo) : ERROR_NO_DATA;
962 if (dwRet == ERROR_BUFFER_OVERFLOW)
963 {
964 IP_ADAPTER_INFO *pAdpInfoNew = (IP_ADAPTER_INFO*)RTMemRealloc(pAdpInfo, cbAdpInfo);
965 if (pAdpInfoNew)
966 {
967 pAdpInfo = pAdpInfoNew;
968 RT_BZERO(pAdpInfo, cbAdpInfo);
969 dwRet = g_pfnGetAdaptersInfo(pAdpInfo, &cbAdpInfo);
970 }
971 }
972 else if (dwRet == ERROR_NO_DATA)
973 {
974 VGSvcVerbose(3, "VMInfo/Network: No network adapters available\n");
975
976 /* If no network adapters available / present in the
977 * system we pretend success to not bail out too early. */
978 RTMemFree(pAdpInfo);
979 pAdpInfo = NULL;
980 cbAdpInfo = 0;
981 dwRet = ERROR_SUCCESS;
982 }
983 if (dwRet != ERROR_SUCCESS)
984 {
985 RTMemFree(pAdpInfo);
986 VGSvcError("VMInfo/Network: Failed to get adapter info: Error %d\n", dwRet);
987 return RTErrConvertFromWin32(dwRet);
988 }
989
990 SOCKET sd = g_pfnWSASocketA(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
991 if (sd == SOCKET_ERROR) /* Socket invalid. */
992 {
993 int wsaErr = g_pfnWSAGetLastError();
994 /* Don't complain/bail out with an error if network stack is not up; can happen
995 * on NT4 due to start up when not connected shares dialogs pop up. */
996 if (WSAENETDOWN == wsaErr)
997 {
998 VGSvcVerbose(0, "VMInfo/Network: Network is not up yet.\n");
999 wsaErr = VINF_SUCCESS;
1000 }
1001 else
1002 VGSvcError("VMInfo/Network: Failed to get a socket: Error %d\n", wsaErr);
1003 if (pAdpInfo)
1004 RTMemFree(pAdpInfo);
1005 return RTErrConvertFromWin32(wsaErr);
1006 }
1007
1008 INTERFACE_INFO aInterfaces[20] = {{0}};
1009 DWORD cbReturned = 0;
1010# ifdef RT_ARCH_X86
1011 /* Workaround for uninitialized variable used in memcpy in GetTcpipInterfaceList
1012 (NT4SP1 at least). It seems to be happy enough with garbages, no failure
1013 returns so far, so we just need to prevent it from crashing by filling the
1014 stack with valid pointer values prior to the API call. */
1015 _asm
1016 {
1017 mov edx, edi
1018 lea eax, aInterfaces
1019 mov [esp - 0x1000], eax
1020 mov [esp - 0x2000], eax
1021 mov ecx, 0x2000/4 - 1
1022 cld
1023 lea edi, [esp - 0x2000]
1024 rep stosd
1025 mov edi, edx
1026 }
1027# endif
1028 int rc = g_pfnWSAIoctl(sd,
1029 SIO_GET_INTERFACE_LIST,
1030 NULL, /* pvInBuffer */
1031 0, /* cbInBuffer */
1032 &aInterfaces[0], /* pvOutBuffer */
1033 sizeof(aInterfaces), /* cbOutBuffer */
1034 &cbReturned,
1035 NULL, /* pOverlapped */
1036 NULL); /* pCompletionRoutine */
1037 if (rc == SOCKET_ERROR)
1038 {
1039 VGSvcError("VMInfo/Network: Failed to WSAIoctl() on socket: Error: %d\n", g_pfnWSAGetLastError());
1040 if (pAdpInfo)
1041 RTMemFree(pAdpInfo);
1042 g_pfnclosesocket(sd);
1043 return RTErrConvertFromWin32(g_pfnWSAGetLastError());
1044 }
1045 g_pfnclosesocket(sd);
1046 int cIfacesSystem = cbReturned / sizeof(INTERFACE_INFO);
1047
1048 /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
1049 for (int i = 0; i < cIfacesSystem; ++i)
1050 {
1051 sockaddr_in *pAddress;
1052 u_long nFlags = 0;
1053 if (aInterfaces[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
1054 continue;
1055 nFlags = aInterfaces[i].iiFlags;
1056 pAddress = (sockaddr_in *)&(aInterfaces[i].iiAddress);
1057 Assert(pAddress);
1058 char szIp[32];
1059 RTStrPrintf(szIp, sizeof(szIp), "%s", g_pfninet_ntoa(pAddress->sin_addr));
1060 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfsReported);
1061 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szIp);
1062
1063 pAddress = (sockaddr_in *) & (aInterfaces[i].iiBroadcastAddress);
1064 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfsReported);
1065 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", g_pfninet_ntoa(pAddress->sin_addr));
1066
1067 pAddress = (sockaddr_in *)&(aInterfaces[i].iiNetmask);
1068 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfsReported);
1069 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", g_pfninet_ntoa(pAddress->sin_addr));
1070
1071 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfsReported);
1072 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, nFlags & IFF_UP ? "Up" : "Down");
1073
1074 if (pAdpInfo)
1075 {
1076 IP_ADAPTER_INFO *pAdp;
1077 for (pAdp = pAdpInfo; pAdp; pAdp = pAdp->Next)
1078 if (!strcmp(pAdp->IpAddressList.IpAddress.String, szIp))
1079 break;
1080
1081 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfsReported);
1082 if (pAdp)
1083 {
1084 char szMac[32];
1085 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
1086 pAdp->Address[0], pAdp->Address[1], pAdp->Address[2],
1087 pAdp->Address[3], pAdp->Address[4], pAdp->Address[5]);
1088 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
1089 }
1090 else
1091 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, NULL);
1092 }
1093
1094 cIfsReported++;
1095 }
1096 if (pAdpInfo)
1097 RTMemFree(pAdpInfo);
1098
1099#elif defined(RT_OS_HAIKU)
1100 /** @todo Haiku: implement network info. retreival */
1101 return VERR_NOT_IMPLEMENTED;
1102
1103#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD)
1104 struct ifaddrs *pIfHead = NULL;
1105
1106 /* Get all available interfaces */
1107 int rc = getifaddrs(&pIfHead);
1108 if (rc < 0)
1109 {
1110 rc = RTErrConvertFromErrno(errno);
1111 VGSvcError("VMInfo/Network: Failed to get all interfaces: Error %Rrc\n");
1112 return rc;
1113 }
1114
1115 /* Loop through all interfaces and set the data. */
1116 for (struct ifaddrs *pIfCurr = pIfHead; pIfCurr; pIfCurr = pIfCurr->ifa_next)
1117 {
1118 /*
1119 * Only AF_INET and no loopback interfaces
1120 */
1121 /** @todo IPv6 interfaces */
1122 if ( pIfCurr->ifa_addr->sa_family == AF_INET
1123 && !(pIfCurr->ifa_flags & IFF_LOOPBACK))
1124 {
1125 char szInetAddr[NI_MAXHOST];
1126
1127 memset(szInetAddr, 0, NI_MAXHOST);
1128 getnameinfo(pIfCurr->ifa_addr, sizeof(struct sockaddr_in),
1129 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1130 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfsReported);
1131 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1132
1133 memset(szInetAddr, 0, NI_MAXHOST);
1134 getnameinfo(pIfCurr->ifa_broadaddr, sizeof(struct sockaddr_in),
1135 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1136 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfsReported);
1137 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1138
1139 memset(szInetAddr, 0, NI_MAXHOST);
1140 getnameinfo(pIfCurr->ifa_netmask, sizeof(struct sockaddr_in),
1141 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1142 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfsReported);
1143 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
1144
1145 /* Search for the AF_LINK interface of the current AF_INET one and get the mac. */
1146 for (struct ifaddrs *pIfLinkCurr = pIfHead; pIfLinkCurr; pIfLinkCurr = pIfLinkCurr->ifa_next)
1147 {
1148 if ( pIfLinkCurr->ifa_addr->sa_family == AF_LINK
1149 && !strcmp(pIfCurr->ifa_name, pIfLinkCurr->ifa_name))
1150 {
1151 char szMac[32];
1152 uint8_t *pu8Mac = NULL;
1153 struct sockaddr_dl *pLinkAddress = (struct sockaddr_dl *)pIfLinkCurr->ifa_addr;
1154
1155 AssertPtr(pLinkAddress);
1156 pu8Mac = (uint8_t *)LLADDR(pLinkAddress);
1157 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
1158 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
1159 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfsReported);
1160 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
1161 break;
1162 }
1163 }
1164
1165 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfsReported);
1166 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down");
1167
1168 cIfsReported++;
1169 }
1170 }
1171
1172 /* Free allocated resources. */
1173 freeifaddrs(pIfHead);
1174
1175#else /* !RT_OS_WINDOWS && !RT_OS_FREEBSD */
1176 /*
1177 * Use SIOCGIFCONF to get a list of interface/protocol configurations.
1178 *
1179 * See "UNIX Network Programming Volume 1" by W. R. Stevens, section 17.6
1180 * for details on this ioctl.
1181 */
1182 int sd = socket(AF_INET, SOCK_DGRAM, 0);
1183 if (sd < 0)
1184 {
1185 int rc = RTErrConvertFromErrno(errno);
1186 VGSvcError("VMInfo/Network: Failed to get a socket: Error %Rrc\n", rc);
1187 return rc;
1188 }
1189
1190 /* Call SIOCGIFCONF with the right sized buffer (remember the size). */
1191 static int s_cbBuf = 256; // 1024
1192 int cbBuf = s_cbBuf;
1193 char *pchBuf;
1194 struct ifconf IfConf;
1195 int rc = VINF_SUCCESS;
1196 for (;;)
1197 {
1198 pchBuf = (char *)RTMemTmpAllocZ(cbBuf);
1199 if (!pchBuf)
1200 {
1201 rc = VERR_NO_TMP_MEMORY;
1202 break;
1203 }
1204
1205 IfConf.ifc_len = cbBuf;
1206 IfConf.ifc_buf = pchBuf;
1207 if (ioctl(sd, SIOCGIFCONF, &IfConf) >= 0)
1208 {
1209 /* Hard to anticipate how space an address might possibly take, so
1210 making some generous assumptions here to avoid performing the
1211 query twice with different buffer sizes. */
1212 if (IfConf.ifc_len + 128 < cbBuf)
1213 break;
1214 }
1215 else if (errno != EOVERFLOW)
1216 {
1217 rc = RTErrConvertFromErrno(errno);
1218 break;
1219 }
1220
1221 /* grow the buffer */
1222 s_cbBuf = cbBuf *= 2;
1223 RTMemFree(pchBuf);
1224 }
1225 if (RT_FAILURE(rc))
1226 {
1227 close(sd);
1228 RTMemTmpFree(pchBuf);
1229 VGSvcError("VMInfo/Network: Error doing SIOCGIFCONF (cbBuf=%d): %Rrc\n", cbBuf, rc);
1230 return rc;
1231 }
1232
1233 /*
1234 * Iterate the interface/protocol configurations.
1235 *
1236 * Note! The current code naively assumes one IPv4 address per interface.
1237 * This means that guest assigning more than one address to an
1238 * interface will get multiple entries for one physical interface.
1239 */
1240# ifdef RT_OS_OS2
1241 struct ifreq *pPrevLinkAddr = NULL;
1242# endif
1243 struct ifreq *pCur = IfConf.ifc_req;
1244 size_t cbLeft = IfConf.ifc_len;
1245 while (cbLeft >= sizeof(*pCur))
1246 {
1247# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
1248 /* These two do not provide the sa_len member but only support address
1249 * families which do not need extra bytes on the end. */
1250# define SA_LEN(pAddr) sizeof(struct sockaddr)
1251# elif !defined(SA_LEN)
1252# define SA_LEN(pAddr) (pAddr)->sa_len
1253# endif
1254 /* Figure the size of the current request. */
1255 size_t cbCur = RT_UOFFSETOF(struct ifreq, ifr_addr)
1256 + SA_LEN(&pCur->ifr_addr);
1257 cbCur = RT_MAX(cbCur, sizeof(struct ifreq));
1258# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX)
1259 Assert(pCur->ifr_addr.sa_family == AF_INET);
1260# endif
1261 AssertBreak(cbCur <= cbLeft);
1262
1263# ifdef RT_OS_OS2
1264 /* On OS/2 we get the MAC address in the AF_LINK that the BSD 4.4 stack
1265 emits. We boldly ASSUME these always comes first. */
1266 if ( pCur->ifr_addr.sa_family == AF_LINK
1267 && ((struct sockaddr_dl *)&pCur->ifr_addr)->sdl_alen == 6)
1268 pPrevLinkAddr = pCur;
1269# endif
1270
1271 /* Skip it if it's not the kind of address we're looking for. */
1272 struct ifreq IfReqTmp;
1273 bool fIfUp = false;
1274 bool fSkip = false;
1275 if (pCur->ifr_addr.sa_family != AF_INET)
1276 fSkip = true;
1277 else
1278 {
1279 /* Get the interface flags so we can detect loopback and check if it's up. */
1280 IfReqTmp = *pCur;
1281 if (ioctl(sd, SIOCGIFFLAGS, &IfReqTmp) < 0)
1282 {
1283 rc = RTErrConvertFromErrno(errno);
1284 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFFLAGS,%s) on socket: Error %Rrc\n", pCur->ifr_name, rc);
1285 break;
1286 }
1287 fIfUp = !!(IfReqTmp.ifr_flags & IFF_UP);
1288 if (IfReqTmp.ifr_flags & IFF_LOOPBACK) /* Skip the loopback device. */
1289 fSkip = true;
1290 }
1291 if (!fSkip)
1292 {
1293 size_t offSubProp = RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32", cIfsReported);
1294
1295 sockaddr_in *pAddress = (sockaddr_in *)&pCur->ifr_addr;
1296 strcpy(&szPropPath[offSubProp], "/V4/IP");
1297 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1298
1299 /* Get the broadcast address. */
1300 IfReqTmp = *pCur;
1301 if (ioctl(sd, SIOCGIFBRDADDR, &IfReqTmp) < 0)
1302 {
1303 rc = RTErrConvertFromErrno(errno);
1304 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %Rrc\n", rc);
1305 break;
1306 }
1307 pAddress = (sockaddr_in *)&IfReqTmp.ifr_broadaddr;
1308 strcpy(&szPropPath[offSubProp], "/V4/Broadcast");
1309 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1310
1311 /* Get the net mask. */
1312 IfReqTmp = *pCur;
1313 if (ioctl(sd, SIOCGIFNETMASK, &IfReqTmp) < 0)
1314 {
1315 rc = RTErrConvertFromErrno(errno);
1316 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFNETMASK) on socket: Error %Rrc\n", rc);
1317 break;
1318 }
1319# if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
1320 pAddress = (sockaddr_in *)&IfReqTmp.ifr_addr;
1321# else
1322 pAddress = (sockaddr_in *)&IfReqTmp.ifr_netmask;
1323# endif
1324 strcpy(&szPropPath[offSubProp], "/V4/Netmask");
1325 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1326
1327# if defined(RT_OS_SOLARIS)
1328 /*
1329 * "ifreq" is obsolete on Solaris. We use the recommended "lifreq".
1330 * We might fail if the interface has not been assigned an IP address.
1331 * That doesn't matter; as long as it's plumbed we can pick it up.
1332 * But, if it has not acquired an IP address we cannot obtain it's MAC
1333 * address this way, so we just use all zeros there.
1334 */
1335 RTMAC IfMac;
1336 struct lifreq IfReq;
1337 RT_ZERO(IfReq);
1338 AssertCompile(sizeof(IfReq.lifr_name) >= sizeof(pCur->ifr_name));
1339 strncpy(IfReq.lifr_name, pCur->ifr_name, sizeof(IfReq.lifr_name));
1340 if (ioctl(sd, SIOCGLIFADDR, &IfReq) >= 0)
1341 {
1342 struct arpreq ArpReq;
1343 RT_ZERO(ArpReq);
1344 memcpy(&ArpReq.arp_pa, &IfReq.lifr_addr, sizeof(struct sockaddr_in));
1345
1346 if (ioctl(sd, SIOCGARP, &ArpReq) >= 0)
1347 memcpy(&IfMac, ArpReq.arp_ha.sa_data, sizeof(IfMac));
1348 else
1349 {
1350 rc = RTErrConvertFromErrno(errno);
1351 VGSvcError("VMInfo/Network: failed to ioctl(SIOCGARP) on socket: Error %Rrc\n", rc);
1352 break;
1353 }
1354 }
1355 else
1356 {
1357 VGSvcVerbose(2, "VMInfo/Network: Interface '%s' has no assigned IP address, skipping ...\n", pCur->ifr_name);
1358 continue;
1359 }
1360# elif defined(RT_OS_OS2)
1361 RTMAC IfMac;
1362 if ( pPrevLinkAddr
1363 && strncmp(pCur->ifr_name, pPrevLinkAddr->ifr_name, sizeof(pCur->ifr_name)) == 0)
1364 {
1365 struct sockaddr_dl *pDlAddr = (struct sockaddr_dl *)&pPrevLinkAddr->ifr_addr;
1366 IfMac = *(PRTMAC)&pDlAddr->sdl_data[pDlAddr->sdl_nlen];
1367 }
1368 else
1369 RT_ZERO(IfMac);
1370#else
1371 if (ioctl(sd, SIOCGIFHWADDR, &IfReqTmp) < 0)
1372 {
1373 rc = RTErrConvertFromErrno(errno);
1374 VGSvcError("VMInfo/Network: Failed to ioctl(SIOCGIFHWADDR) on socket: Error %Rrc\n", rc);
1375 break;
1376 }
1377 RTMAC IfMac = *(PRTMAC)&IfReqTmp.ifr_hwaddr.sa_data[0];
1378# endif
1379 strcpy(&szPropPath[offSubProp], "/MAC");
1380 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%02X%02X%02X%02X%02X%02X",
1381 IfMac.au8[0], IfMac.au8[1], IfMac.au8[2], IfMac.au8[3], IfMac.au8[4], IfMac.au8[5]);
1382
1383 strcpy(&szPropPath[offSubProp], "/Status");
1384 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, fIfUp ? "Up" : "Down");
1385
1386 /* The name. */
1387 int rc2 = RTStrValidateEncodingEx(pCur->ifr_name, sizeof(pCur->ifr_name), 0);
1388 if (RT_SUCCESS(rc2))
1389 {
1390 strcpy(&szPropPath[offSubProp], "/Name");
1391 VGSvcPropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%.*s", sizeof(pCur->ifr_name), pCur->ifr_name);
1392 }
1393
1394 cIfsReported++;
1395 }
1396
1397 /*
1398 * Next interface/protocol configuration.
1399 */
1400 pCur = (struct ifreq *)((uintptr_t)pCur + cbCur);
1401 cbLeft -= cbCur;
1402 }
1403
1404 RTMemTmpFree(pchBuf);
1405 close(sd);
1406 if (RT_FAILURE(rc))
1407 VGSvcError("VMInfo/Network: Network enumeration for interface %RU32 failed with error %Rrc\n", cIfsReported, rc);
1408
1409#endif /* !RT_OS_WINDOWS */
1410
1411#if 0 /* Zapping not enabled yet, needs more testing first. */
1412 /*
1413 * Zap all stale network interface data if the former (saved) network ifaces count
1414 * is bigger than the current one.
1415 */
1416
1417 /* Get former count. */
1418 uint32_t cIfsReportedOld;
1419 rc = VGSvcReadPropUInt32(g_uVMInfoGuestPropSvcClientID, g_pszPropCacheValNetCount, &cIfsReportedOld,
1420 0 /* Min */, UINT32_MAX /* Max */);
1421 if ( RT_SUCCESS(rc)
1422 && cIfsReportedOld > cIfsReported) /* Are some ifaces not around anymore? */
1423 {
1424 VGSvcVerbose(3, "VMInfo/Network: Stale interface data detected (%RU32 old vs. %RU32 current)\n",
1425 cIfsReportedOld, cIfsReported);
1426
1427 uint32_t uIfaceDeleteIdx = cIfsReported;
1428 do
1429 {
1430 VGSvcVerbose(3, "VMInfo/Network: Deleting stale data of interface %d ...\n", uIfaceDeleteIdx);
1431 rc = VGSvcPropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%RU32", uIfaceDeleteIdx++);
1432 } while (RT_SUCCESS(rc));
1433 }
1434 else if ( RT_FAILURE(rc)
1435 && rc != VERR_NOT_FOUND)
1436 {
1437 VGSvcError("VMInfo/Network: Failed retrieving old network interfaces count with error %Rrc\n", rc);
1438 }
1439#endif
1440
1441 /*
1442 * This property is a beacon which is _always_ written, even if the network configuration
1443 * does not change. If this property is missing, the host assumes that all other GuestInfo
1444 * properties are no longer valid.
1445 */
1446 VGSvcPropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNetCount, "%RU32", cIfsReported);
1447
1448 /* Don't fail here; just report everything we got. */
1449 return VINF_SUCCESS;
1450}
1451
1452
1453/**
1454 * @interface_method_impl{VBOXSERVICE,pfnWorker}
1455 */
1456static DECLCALLBACK(int) vbsvcVMInfoWorker(bool volatile *pfShutdown)
1457{
1458 int rc;
1459
1460 /*
1461 * Tell the control thread that it can continue
1462 * spawning services.
1463 */
1464 RTThreadUserSignal(RTThreadSelf());
1465
1466#ifdef RT_OS_WINDOWS
1467 /* Required for network information (must be called per thread). */
1468 if (g_pfnWSAStartup)
1469 {
1470 WSADATA wsaData;
1471 RT_ZERO(wsaData);
1472 if (g_pfnWSAStartup(MAKEWORD(2, 2), &wsaData))
1473 VGSvcError("VMInfo/Network: WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(g_pfnWSAGetLastError()));
1474 }
1475#endif
1476
1477 /*
1478 * Write the fixed properties first.
1479 */
1480 vgsvcVMInfoWriteFixedProperties();
1481
1482 /*
1483 * Now enter the loop retrieving runtime data continuously.
1484 */
1485 for (;;)
1486 {
1487 rc = vgsvcVMInfoWriteUsers();
1488 if (RT_FAILURE(rc))
1489 break;
1490
1491 rc = vgsvcVMInfoWriteNetwork();
1492 if (RT_FAILURE(rc))
1493 break;
1494
1495 /* Whether to wait for event semaphore or not. */
1496 bool fWait = true;
1497
1498 /* Check for location awareness. This most likely only
1499 * works with VBox (latest) 4.1 and up. */
1500
1501 /* Check for new connection. */
1502 char *pszLAClientID = NULL;
1503 int rc2 = VGSvcReadHostProp(g_uVMInfoGuestPropSvcClientID, g_pszLAActiveClient, true /* Read only */,
1504 &pszLAClientID, NULL /* Flags */, NULL /* Timestamp */);
1505 if (RT_SUCCESS(rc2))
1506 {
1507 AssertPtr(pszLAClientID);
1508 if (RTStrICmp(pszLAClientID, "0")) /* Is a client connected? */
1509 {
1510 uint32_t uLAClientID = RTStrToInt32(pszLAClientID);
1511 uint64_t uLAClientAttachedTS;
1512
1513 /* Peek at "Attach" value to figure out if hotdesking happened. */
1514 char *pszAttach = NULL;
1515 rc2 = vgsvcGetLAClientValue(uLAClientID, "Attach", &pszAttach,
1516 &uLAClientAttachedTS);
1517
1518 if ( RT_SUCCESS(rc2)
1519 && ( !g_LAClientAttachedTS
1520 || (g_LAClientAttachedTS != uLAClientAttachedTS)))
1521 {
1522 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1523
1524 /* Note: There is a race between setting the guest properties by the host and getting them by
1525 * the guest. */
1526 rc2 = vgsvcGetLAClientInfo(uLAClientID, &g_LAClientInfo);
1527 if (RT_SUCCESS(rc2))
1528 {
1529 VGSvcVerbose(1, "VRDP: Hotdesk client %s with ID=%RU32, Name=%s, Domain=%s\n",
1530 /* If g_LAClientAttachedTS is 0 this means there already was an active
1531 * hotdesk session when VBoxService started. */
1532 !g_LAClientAttachedTS ? "already active" : g_LAClientInfo.fAttached ? "connected" : "disconnected",
1533 uLAClientID, g_LAClientInfo.pszName, g_LAClientInfo.pszDomain);
1534
1535 g_LAClientAttachedTS = g_LAClientInfo.uAttachedTS;
1536
1537 /* Don't wait for event semaphore below anymore because we now know that the client
1538 * changed. This means we need to iterate all VM information again immediately. */
1539 fWait = false;
1540 }
1541 else
1542 {
1543 static int s_iBitchedAboutLAClientInfo = 0;
1544 if (s_iBitchedAboutLAClientInfo < 10)
1545 {
1546 s_iBitchedAboutLAClientInfo++;
1547 VGSvcError("Error getting active location awareness client info, rc=%Rrc\n", rc2);
1548 }
1549 }
1550 }
1551 else if (RT_FAILURE(rc2))
1552 VGSvcError("Error getting attached value of location awareness client %RU32, rc=%Rrc\n", uLAClientID, rc2);
1553 if (pszAttach)
1554 RTStrFree(pszAttach);
1555 }
1556 else
1557 {
1558 VGSvcVerbose(1, "VRDP: UTTSC disconnected from VRDP server\n");
1559 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1560 }
1561
1562 RTStrFree(pszLAClientID);
1563 }
1564 else
1565 {
1566 static int s_iBitchedAboutLAClient = 0;
1567 if ( (rc2 != VERR_NOT_FOUND) /* No location awareness installed, skip. */
1568 && s_iBitchedAboutLAClient < 3)
1569 {
1570 s_iBitchedAboutLAClient++;
1571 VGSvcError("VRDP: Querying connected location awareness client failed with rc=%Rrc\n", rc2);
1572 }
1573 }
1574
1575 VGSvcVerbose(3, "VRDP: Handling location awareness done\n");
1576
1577 /*
1578 * Flush all properties if we were restored.
1579 */
1580 uint64_t idNewSession = g_idVMInfoSession;
1581 VbglR3GetSessionId(&idNewSession);
1582 if (idNewSession != g_idVMInfoSession)
1583 {
1584 VGSvcVerbose(3, "The VM session ID changed, flushing all properties\n");
1585 vgsvcVMInfoWriteFixedProperties();
1586 VGSvcPropCacheFlush(&g_VMInfoPropCache);
1587 g_idVMInfoSession = idNewSession;
1588 }
1589
1590 /*
1591 * Block for a while.
1592 *
1593 * The event semaphore takes care of ignoring interruptions and it
1594 * allows us to implement service wakeup later.
1595 */
1596 if (*pfShutdown)
1597 break;
1598 if (fWait)
1599 rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval);
1600 if (*pfShutdown)
1601 break;
1602 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
1603 {
1604 VGSvcError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
1605 rc = rc2;
1606 break;
1607 }
1608 else if (RT_LIKELY(RT_SUCCESS(rc2)))
1609 {
1610 /* Reset event semaphore if it got triggered. */
1611 rc2 = RTSemEventMultiReset(g_hVMInfoEvent);
1612 if (RT_FAILURE(rc2))
1613 rc2 = VGSvcError("RTSemEventMultiReset failed; rc2=%Rrc\n", rc2);
1614 }
1615 }
1616
1617#ifdef RT_OS_WINDOWS
1618 if (g_pfnWSACleanup)
1619 g_pfnWSACleanup();
1620#endif
1621
1622 return rc;
1623}
1624
1625
1626/**
1627 * @interface_method_impl{VBOXSERVICE,pfnStop}
1628 */
1629static DECLCALLBACK(void) vbsvcVMInfoStop(void)
1630{
1631 RTSemEventMultiSignal(g_hVMInfoEvent);
1632}
1633
1634
1635/**
1636 * @interface_method_impl{VBOXSERVICE,pfnTerm}
1637 */
1638static DECLCALLBACK(void) vbsvcVMInfoTerm(void)
1639{
1640 if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
1641 {
1642 /** @todo temporary solution: Zap all values which are not valid
1643 * anymore when VM goes down (reboot/shutdown ). Needs to
1644 * be replaced with "temporary properties" later.
1645 *
1646 * One idea is to introduce a (HGCM-)session guest property
1647 * flag meaning that a guest property is only valid as long
1648 * as the HGCM session isn't closed (e.g. guest application
1649 * terminates). [don't remove till implemented]
1650 */
1651 /** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and use the cache
1652 * since it remembers what we've written. */
1653 /* Delete the "../Net" branch. */
1654 const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
1655 int rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
1656
1657 /* Destroy LA client info. */
1658 vgsvcFreeLAClientInfo(&g_LAClientInfo);
1659
1660 /* Destroy property cache. */
1661 VGSvcPropCacheDestroy(&g_VMInfoPropCache);
1662
1663 /* Disconnect from guest properties service. */
1664 rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID);
1665 if (RT_FAILURE(rc))
1666 VGSvcError("Failed to disconnect from guest property service! Error: %Rrc\n", rc);
1667 g_uVMInfoGuestPropSvcClientID = 0;
1668
1669 RTSemEventMultiDestroy(g_hVMInfoEvent);
1670 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
1671 }
1672}
1673
1674
1675/**
1676 * The 'vminfo' service description.
1677 */
1678VBOXSERVICE g_VMInfo =
1679{
1680 /* pszName. */
1681 "vminfo",
1682 /* pszDescription. */
1683 "Virtual Machine Information",
1684 /* pszUsage. */
1685 " [--vminfo-interval <ms>] [--vminfo-user-idle-threshold <ms>]"
1686 ,
1687 /* pszOptions. */
1688 " --vminfo-interval Specifies the interval at which to retrieve the\n"
1689 " VM information. The default is 10000 ms.\n"
1690 " --vminfo-user-idle-threshold <ms>\n"
1691 " Specifies the user idle threshold (in ms) for\n"
1692 " considering a guest user as being idle. The default\n"
1693 " is 5000 (5 seconds).\n"
1694 ,
1695 /* methods */
1696 vbsvcVMInfoPreInit,
1697 vbsvcVMInfoOption,
1698 vbsvcVMInfoInit,
1699 vbsvcVMInfoWorker,
1700 vbsvcVMInfoStop,
1701 vbsvcVMInfoTerm
1702};
1703
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette