VirtualBox

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

Last change on this file since 72097 was 70196, checked in by vboxsync, 7 years ago

VBoxService: Made it work on NT 3.51.

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