VirtualBox

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

最後變更 在這個檔案從101233是 101233,由 vboxsync 提交於 18 月 前

Additions/VBoxService/VBoxServiceVMInfo.cpp: implement user detection via systemd-logind "org.freedesktop.login1" DBus protocol

Also cleaned up old ConsoleKit code bugs & todos. bugref:6492

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

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