VirtualBox

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

Last change on this file since 92667 was 87846, checked in by vboxsync, 4 years ago

VBoxService/VMInfo: Fix a call to strncpy(3C) to specify the size of the destination buffer rather than the source buffer.

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