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