VirtualBox

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

Last change on this file since 45874 was 45874, checked in by vboxsync, 12 years ago

VBoxService: More logging for property cache / user enumeration.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.7 KB
Line 
1/* $Id: VBoxServiceVMInfo.cpp 45874 2013-05-02 12:16:42Z vboxsync $ */
2/** @file
3 * VBoxService - Virtual Machine Information for the Host.
4 */
5
6/*
7 * Copyright (C) 2009-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#ifdef RT_OS_WINDOWS
24# ifdef TARGET_NT4 /* HACK ALERT! PMIB_IPSTATS undefined if 0x0400 with newer SDKs. */
25# undef _WIN32_WINNT
26# define _WIN32_WINNT 0x0500
27# endif
28# include <winsock2.h>
29# include <iphlpapi.h>
30# include <ws2tcpip.h>
31# include <windows.h>
32# include <Ntsecapi.h>
33#else
34# define __STDC_LIMIT_MACROS
35# include <arpa/inet.h>
36# include <errno.h>
37# include <netinet/in.h>
38# include <sys/ioctl.h>
39# include <sys/socket.h>
40# include <net/if.h>
41# include <pwd.h> /* getpwuid */
42# include <unistd.h>
43# if !defined(RT_OS_OS2) && !defined(RT_OS_FREEBSD) && !defined(RT_OS_HAIKU)
44# include <utmpx.h> /* @todo FreeBSD 9 should have this. */
45# endif
46# ifdef RT_OS_SOLARIS
47# include <sys/sockio.h>
48# include <net/if_arp.h>
49# endif
50# if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
51# include <ifaddrs.h> /* getifaddrs, freeifaddrs */
52# include <net/if_dl.h> /* LLADDR */
53# include <netdb.h> /* getnameinfo */
54# endif
55# ifdef VBOX_WITH_DBUS
56# include <VBox/dbus.h>
57# endif
58#endif
59
60#include <iprt/mem.h>
61#include <iprt/thread.h>
62#include <iprt/string.h>
63#include <iprt/semaphore.h>
64#include <iprt/system.h>
65#include <iprt/time.h>
66#include <iprt/assert.h>
67#include <VBox/version.h>
68#include <VBox/VBoxGuestLib.h>
69#include "VBoxServiceInternal.h"
70#include "VBoxServiceUtils.h"
71#include "VBoxServicePropCache.h"
72
73
74/** Structure containing information about a location awarness
75 * client provided by the host. */
76/** @todo Move this (and functions) into VbglR3. */
77typedef struct VBOXSERVICELACLIENTINFO
78{
79 uint32_t uID;
80 char *pszName;
81 char *pszLocation;
82 char *pszDomain;
83 bool fAttached;
84 uint64_t uAttachedTS;
85} VBOXSERVICELACLIENTINFO, *PVBOXSERVICELACLIENTINFO;
86
87
88/*******************************************************************************
89* Global Variables *
90*******************************************************************************/
91/** The vminfo interval (milliseconds). */
92static uint32_t g_cMsVMInfoInterval = 0;
93/** The semaphore we're blocking on. */
94static RTSEMEVENTMULTI g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
95/** The guest property service client ID. */
96static uint32_t g_uVMInfoGuestPropSvcClientID = 0;
97/** Number of currently logged in users in OS. */
98static uint32_t g_cVMInfoLoggedInUsers = 0;
99/** The guest property cache. */
100static VBOXSERVICEVEPROPCACHE g_VMInfoPropCache;
101static const char *g_pszPropCacheValLoggedInUsersList = "/VirtualBox/GuestInfo/OS/LoggedInUsersList";
102static const char *g_pszPropCacheValLoggedInUsers = "/VirtualBox/GuestInfo/OS/LoggedInUsers";
103static const char *g_pszPropCacheValNoLoggedInUsers = "/VirtualBox/GuestInfo/OS/NoLoggedInUsers";
104static const char *g_pszPropCacheValNetCount = "/VirtualBox/GuestInfo/Net/Count";
105/** The VM session ID. Changes whenever the VM is restored or reset. */
106static uint64_t g_idVMInfoSession;
107/** The last attached locartion awareness (LA) client timestamp. */
108static uint64_t g_LAClientAttachedTS = 0;
109/** The current LA client info. */
110static VBOXSERVICELACLIENTINFO g_LAClientInfo;
111
112
113/*******************************************************************************
114* Defines *
115*******************************************************************************/
116static const char *g_pszLAActiveClient = "/VirtualBox/HostInfo/VRDP/ActiveClient";
117
118#ifdef VBOX_WITH_DBUS
119/** ConsoleKit defines (taken from 0.4.5). */
120#define CK_NAME "org.freedesktop.ConsoleKit"
121#define CK_PATH "/org/freedesktop/ConsoleKit"
122#define CK_INTERFACE "org.freedesktop.ConsoleKit"
123
124#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
125#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
126#define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat"
127#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
128#endif
129
130
131
132/**
133 * Signals the event so that a re-enumeration of VM-specific
134 * information (like logged in users) can happen.
135 *
136 * @return IPRT status code.
137 */
138int VBoxServiceVMInfoSignal(void)
139{
140 /* Trigger a re-enumeration of all logged-in users by unblocking
141 * the multi event semaphore of the VMInfo thread. */
142 if (g_hVMInfoEvent)
143 return RTSemEventMultiSignal(g_hVMInfoEvent);
144
145 return VINF_SUCCESS;
146}
147
148
149/** @copydoc VBOXSERVICE::pfnPreInit */
150static DECLCALLBACK(int) VBoxServiceVMInfoPreInit(void)
151{
152 return VINF_SUCCESS;
153}
154
155
156/** @copydoc VBOXSERVICE::pfnOption */
157static DECLCALLBACK(int) VBoxServiceVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi)
158{
159 int rc = -1;
160 if (ppszShort)
161 /* no short options */;
162 else if (!strcmp(argv[*pi], "--vminfo-interval"))
163 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
164 &g_cMsVMInfoInterval, 1, UINT32_MAX - 1);
165 return rc;
166}
167
168
169/** @copydoc VBOXSERVICE::pfnInit */
170static DECLCALLBACK(int) VBoxServiceVMInfoInit(void)
171{
172 /*
173 * If not specified, find the right interval default.
174 * Then create the event sem to block on.
175 */
176 if (!g_cMsVMInfoInterval)
177 g_cMsVMInfoInterval = g_DefaultInterval * 1000;
178 if (!g_cMsVMInfoInterval)
179 {
180 /* Set it to 5s by default for location awareness checks. */
181 g_cMsVMInfoInterval = 5 * 1000;
182 }
183
184 int rc = RTSemEventMultiCreate(&g_hVMInfoEvent);
185 AssertRCReturn(rc, rc);
186
187 VbglR3GetSessionId(&g_idVMInfoSession);
188 /* The status code is ignored as this information is not available with VBox < 3.2.10. */
189
190 /* Initialize the LA client object. */
191 RT_ZERO(g_LAClientInfo);
192
193 rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID);
194 if (RT_SUCCESS(rc))
195 VBoxServiceVerbose(3, "Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID);
196 else
197 {
198 /* If the service was not found, we disable this service without
199 causing VBoxService to fail. */
200 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
201 {
202 VBoxServiceVerbose(0, "Guest property service is not available, disabling the service\n");
203 rc = VERR_SERVICE_DISABLED;
204 }
205 else
206 VBoxServiceError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
207 RTSemEventMultiDestroy(g_hVMInfoEvent);
208 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
209 }
210
211 if (RT_SUCCESS(rc))
212 {
213 VBoxServicePropCacheCreate(&g_VMInfoPropCache, g_uVMInfoGuestPropSvcClientID);
214
215 /*
216 * Declare some guest properties with flags and reset values.
217 */
218 int rc2 = VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList,
219 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, NULL /* Delete on exit */);
220 if (RT_FAILURE(rc2))
221 VBoxServiceError("Failed to init property cache value \"%s\", rc=%Rrc\n", g_pszPropCacheValLoggedInUsersList, rc2);
222
223 rc2 = VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers,
224 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "0");
225 if (RT_FAILURE(rc2))
226 VBoxServiceError("Failed to init property cache value \"%s\", rc=%Rrc\n", g_pszPropCacheValLoggedInUsers, rc2);
227
228 rc2 = VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers,
229 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "true");
230 if (RT_FAILURE(rc2))
231 VBoxServiceError("Failed to init property cache value \"%s\", rc=%Rrc\n", g_pszPropCacheValNoLoggedInUsers, rc2);
232
233 rc2 = VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValNetCount,
234 VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */);
235 if (RT_FAILURE(rc2))
236 VBoxServiceError("Failed to init property cache value \"%s\", rc=%Rrc\n", g_pszPropCacheValNetCount, rc2);
237 }
238 return rc;
239}
240
241
242/**
243 * Retrieves a specifiy client LA property.
244 *
245 * @return IPRT status code.
246 * @param uClientID LA client ID to retrieve property for.
247 * @param pszProperty Property (without path) to retrieve.
248 * @param ppszValue Where to store value of property.
249 * @param puTimestamp Timestamp of property to retrieve. Optional.
250 */
251static int vboxServiceGetLAClientValue(uint32_t uClientID, const char *pszProperty,
252 char **ppszValue, uint64_t *puTimestamp)
253{
254 AssertReturn(uClientID, VERR_INVALID_PARAMETER);
255 AssertPtrReturn(pszProperty, VERR_INVALID_POINTER);
256
257 int rc;
258
259 char pszClientPath[255];
260 if (RTStrPrintf(pszClientPath, sizeof(pszClientPath),
261 "/VirtualBox/HostInfo/VRDP/Client/%RU32/%s", uClientID, pszProperty))
262 {
263 rc = VBoxServiceReadHostProp(g_uVMInfoGuestPropSvcClientID, pszClientPath, true /* Read only */,
264 ppszValue, NULL /* Flags */, puTimestamp);
265 }
266 else
267 rc = VERR_NO_MEMORY;
268
269 return rc;
270}
271
272
273/**
274 * Retrieves LA client information. On success the returned structure will have allocated
275 * objects which need to be free'd with vboxServiceFreeLAClientInfo.
276 *
277 * @return IPRT status code.
278 * @param uClientID Client ID to retrieve information for.
279 * @param pClient Pointer where to store the client information.
280 */
281static int vboxServiceGetLAClientInfo(uint32_t uClientID, PVBOXSERVICELACLIENTINFO pClient)
282{
283 AssertReturn(uClientID, VERR_INVALID_PARAMETER);
284 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
285
286 int rc = vboxServiceGetLAClientValue(uClientID, "Name", &pClient->pszName,
287 NULL /* Timestamp */);
288 if (RT_SUCCESS(rc))
289 {
290 char *pszAttach;
291 rc = vboxServiceGetLAClientValue(uClientID, "Attach", &pszAttach,
292 &pClient->uAttachedTS);
293 if (RT_SUCCESS(rc))
294 {
295 AssertPtr(pszAttach);
296 pClient->fAttached = !RTStrICmp(pszAttach, "1") ? true : false;
297
298 RTStrFree(pszAttach);
299 }
300 }
301 if (RT_SUCCESS(rc))
302 rc = vboxServiceGetLAClientValue(uClientID, "Location", &pClient->pszLocation,
303 NULL /* Timestamp */);
304 if (RT_SUCCESS(rc))
305 rc = vboxServiceGetLAClientValue(uClientID, "Domain", &pClient->pszDomain,
306 NULL /* Timestamp */);
307 if (RT_SUCCESS(rc))
308 pClient->uID = uClientID;
309
310 return rc;
311}
312
313
314/**
315 * Frees all allocated LA client information of a structure.
316 *
317 * @param pClient Pointer to client information structure to free.
318 */
319static void vboxServiceFreeLAClientInfo(PVBOXSERVICELACLIENTINFO pClient)
320{
321 if (pClient)
322 {
323 if (pClient->pszName)
324 {
325 RTStrFree(pClient->pszName);
326 pClient->pszName = NULL;
327 }
328 if (pClient->pszLocation)
329 {
330 RTStrFree(pClient->pszLocation);
331 pClient->pszLocation = NULL;
332 }
333 if (pClient->pszDomain)
334 {
335 RTStrFree(pClient->pszDomain);
336 pClient->pszDomain = NULL;
337 }
338 }
339}
340
341
342/**
343 * Writes the properties that won't change while the service is running.
344 *
345 * Errors are ignored.
346 */
347static void vboxserviceVMInfoWriteFixedProperties(void)
348{
349 /*
350 * First get OS information that won't change.
351 */
352 char szInfo[256];
353 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szInfo, sizeof(szInfo));
354 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Product",
355 "%s", RT_FAILURE(rc) ? "" : szInfo);
356
357 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szInfo, sizeof(szInfo));
358 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Release",
359 "%s", RT_FAILURE(rc) ? "" : szInfo);
360
361 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szInfo, sizeof(szInfo));
362 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/Version",
363 "%s", RT_FAILURE(rc) ? "" : szInfo);
364
365 rc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szInfo, sizeof(szInfo));
366 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/OS/ServicePack",
367 "%s", RT_FAILURE(rc) ? "" : szInfo);
368
369 /*
370 * Retrieve version information about Guest Additions and installed files (components).
371 */
372 char *pszAddVer;
373 char *pszAddVerExt;
374 char *pszAddRev;
375 rc = VbglR3GetAdditionsVersion(&pszAddVer, &pszAddVerExt, &pszAddRev);
376 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Version",
377 "%s", RT_FAILURE(rc) ? "" : pszAddVer);
378 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/VersionExt",
379 "%s", RT_FAILURE(rc) ? "" : pszAddVerExt);
380 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/Revision",
381 "%s", RT_FAILURE(rc) ? "" : pszAddRev);
382 if (RT_SUCCESS(rc))
383 {
384 RTStrFree(pszAddVer);
385 RTStrFree(pszAddVerExt);
386 RTStrFree(pszAddRev);
387 }
388
389#ifdef RT_OS_WINDOWS
390 /*
391 * Do windows specific properties.
392 */
393 char *pszInstDir;
394 rc = VbglR3GetAdditionsInstallationPath(&pszInstDir);
395 VBoxServiceWritePropF(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/InstallDir",
396 "%s", RT_FAILURE(rc) ? "" : pszInstDir);
397 if (RT_SUCCESS(rc))
398 RTStrFree(pszInstDir);
399
400 VBoxServiceWinGetComponentVersions(g_uVMInfoGuestPropSvcClientID);
401#endif
402}
403
404#if defined(VBOX_WITH_DBUS) && defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
405/*
406 * Simple wrapper to work around compiler-specific va_list madness.
407 */
408static dbus_bool_t vboxService_dbus_message_get_args(DBusMessage *message,
409 DBusError *error,
410 int first_arg_type,
411 ...)
412{
413 va_list va;
414 va_start(va, first_arg_type);
415 dbus_bool_t ret = dbus_message_get_args_valist(message, error,
416 first_arg_type, va);
417 va_end(va);
418 return ret;
419}
420#endif
421
422/**
423 * Provide information about active users.
424 */
425static int vboxserviceVMInfoWriteUsers(void)
426{
427 int rc = VINF_SUCCESS;
428 char *pszUserList = NULL;
429 uint32_t cUsersInList = 0;
430
431#ifdef RT_OS_WINDOWS
432# ifndef TARGET_NT4
433 rc = VBoxServiceVMInfoWinWriteUsers(&pszUserList, &cUsersInList);
434# else
435 rc = VERR_NOT_IMPLEMENTED;
436# endif
437
438#elif defined(RT_OS_FREEBSD)
439 /** @todo FreeBSD: Port logged on user info retrieval.
440 * However, FreeBSD 9 supports utmpx, so we could use the code
441 * block below (?). */
442 rc = VERR_NOT_IMPLEMENTED;
443
444#elif defined(RT_OS_HAIKU)
445 /** @todo Haiku: Port logged on user info retrieval. */
446 rc = VERR_NOT_IMPLEMENTED;
447
448#elif defined(RT_OS_OS2)
449 /** @todo OS/2: Port logged on (LAN/local/whatever) user info retrieval. */
450 rc = VERR_NOT_IMPLEMENTED;
451
452#else
453 setutxent();
454 utmpx *ut_user;
455 uint32_t cListSize = 32;
456
457 /* Allocate a first array to hold 32 users max. */
458 char **papszUsers = (char **)RTMemAllocZ(cListSize * sizeof(char *));
459 if (papszUsers == NULL)
460 rc = VERR_NO_MEMORY;
461
462 /* Process all entries in the utmp file.
463 * Note: This only handles */
464 while ( (ut_user = getutxent())
465 && RT_SUCCESS(rc))
466 {
467#ifdef RT_OS_DARWIN /* No ut_user->ut_session on Darwin */
468 VBoxServiceVerbose(4, "Found entry \"%s\" (type: %d, PID: %RU32)\n",
469 ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid);
470#else
471 VBoxServiceVerbose(4, "Found entry \"%s\" (type: %d, PID: %RU32, session: %RU32)\n",
472 ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid, ut_user->ut_session);
473#endif
474 if (cUsersInList > cListSize)
475 {
476 cListSize += 32;
477 void *pvNew = RTMemRealloc(papszUsers, cListSize * sizeof(char*));
478 AssertPtrBreakStmt(pvNew, cListSize -= 32);
479 papszUsers = (char **)pvNew;
480 }
481
482 /* Make sure we don't add user names which are not
483 * part of type USER_PROCES. */
484 if (ut_user->ut_type == USER_PROCESS) /* Regular user process. */
485 {
486 bool fFound = false;
487 for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
488 fFound = strcmp(papszUsers[i], ut_user->ut_user) == 0;
489
490 if (!fFound)
491 {
492 VBoxServiceVerbose(4, "Adding user \"%s\" (type: %d) to list\n",
493 ut_user->ut_user, ut_user->ut_type);
494
495 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ut_user->ut_user);
496 if (RT_FAILURE(rc))
497 break;
498 cUsersInList++;
499 }
500 }
501 }
502
503#ifdef VBOX_WITH_DBUS
504# if defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */
505 DBusError dbErr;
506 DBusConnection *pConnection = NULL;
507 int rc2 = RTDBusLoadLib();
508 if (RT_SUCCESS(rc2))
509 {
510 /* Handle desktop sessions using ConsoleKit. */
511 VBoxServiceVerbose(4, "Checking ConsoleKit sessions ...\n");
512
513 dbus_error_init(&dbErr);
514 pConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbErr);
515 }
516
517 if ( pConnection
518 && !dbus_error_is_set(&dbErr))
519 {
520 /* Get all available sessions. */
521 DBusMessage *pMsgSessions = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
522 "/org/freedesktop/ConsoleKit/Manager",
523 "org.freedesktop.ConsoleKit.Manager",
524 "GetSessions");
525 if ( pMsgSessions
526 && (dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL))
527 {
528 DBusMessage *pReplySessions = dbus_connection_send_with_reply_and_block(pConnection,
529 pMsgSessions, 30 * 1000 /* 30s timeout */,
530 &dbErr);
531 if ( pReplySessions
532 && !dbus_error_is_set(&dbErr))
533 {
534 char **ppszSessions; int cSessions;
535 if ( (dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL)
536 && vboxService_dbus_message_get_args(pReplySessions, &dbErr, DBUS_TYPE_ARRAY,
537 DBUS_TYPE_OBJECT_PATH, &ppszSessions, &cSessions,
538 DBUS_TYPE_INVALID /* Termination */))
539 {
540 VBoxServiceVerbose(4, "ConsoleKit: retrieved %RU16 session(s)\n", cSessions);
541
542 char **ppszCurSession = ppszSessions;
543 for (ppszCurSession;
544 ppszCurSession && *ppszCurSession; ppszCurSession++)
545 {
546 VBoxServiceVerbose(4, "ConsoleKit: processing session '%s' ...\n", *ppszCurSession);
547
548 /* Only respect active sessions .*/
549 bool fActive = false;
550 DBusMessage *pMsgSessionActive = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
551 *ppszCurSession,
552 "org.freedesktop.ConsoleKit.Session",
553 "IsActive");
554 if ( pMsgSessionActive
555 && dbus_message_get_type(pMsgSessionActive) == DBUS_MESSAGE_TYPE_METHOD_CALL)
556 {
557 DBusMessage *pReplySessionActive = dbus_connection_send_with_reply_and_block(pConnection,
558 pMsgSessionActive, 30 * 1000 /* 30s timeout */,
559 &dbErr);
560 if ( pReplySessionActive
561 && !dbus_error_is_set(&dbErr))
562 {
563 DBusMessageIter itMsg;
564 if ( dbus_message_iter_init(pReplySessionActive, &itMsg)
565 && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_BOOLEAN)
566 {
567 /* Get uid from message. */
568 int val;
569 dbus_message_iter_get_basic(&itMsg, &val);
570 fActive = val >= 1;
571 }
572
573 if (pReplySessionActive)
574 dbus_message_unref(pReplySessionActive);
575 }
576
577 if (pMsgSessionActive)
578 dbus_message_unref(pMsgSessionActive);
579 }
580
581 VBoxServiceVerbose(4, "ConsoleKit: session '%s' is %s\n",
582 *ppszCurSession, fActive ? "active" : "not active");
583
584 /* *ppszCurSession now contains the object path
585 * (e.g. "/org/freedesktop/ConsoleKit/Session1"). */
586 DBusMessage *pMsgUnixUser = dbus_message_new_method_call("org.freedesktop.ConsoleKit",
587 *ppszCurSession,
588 "org.freedesktop.ConsoleKit.Session",
589 "GetUnixUser");
590 if ( fActive
591 && pMsgUnixUser
592 && dbus_message_get_type(pMsgUnixUser) == DBUS_MESSAGE_TYPE_METHOD_CALL)
593 {
594 DBusMessage *pReplyUnixUser = dbus_connection_send_with_reply_and_block(pConnection,
595 pMsgUnixUser, 30 * 1000 /* 30s timeout */,
596 &dbErr);
597 if ( pReplyUnixUser
598 && !dbus_error_is_set(&dbErr))
599 {
600 DBusMessageIter itMsg;
601 if ( dbus_message_iter_init(pReplyUnixUser, &itMsg)
602 && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_UINT32)
603 {
604 /* Get uid from message. */
605 uint32_t uid;
606 dbus_message_iter_get_basic(&itMsg, &uid);
607
608 /** @todo Add support for getting UID_MIN (/etc/login.defs on
609 * Debian). */
610 uint32_t uid_min = 1000;
611
612 /* Look up user name (realname) from uid. */
613 setpwent();
614 struct passwd *ppwEntry = getpwuid(uid);
615 if ( ppwEntry
616 && ppwEntry->pw_uid >= uid_min /* Only respect users, not daemons etc. */
617 && ppwEntry->pw_name)
618 {
619 VBoxServiceVerbose(4, "ConsoleKit: session '%s' -> %s (uid: %RU32)\n",
620 *ppszCurSession, ppwEntry->pw_name, uid);
621
622 bool fFound = false;
623 for (uint32_t i = 0; i < cUsersInList && !fFound; i++)
624 fFound = strcmp(papszUsers[i], ppwEntry->pw_name) == 0;
625
626 if (!fFound)
627 {
628 VBoxServiceVerbose(4, "ConsoleKit: adding user \"%s\" to list\n",
629 ppwEntry->pw_name);
630
631 rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ppwEntry->pw_name);
632 if (RT_FAILURE(rc))
633 break;
634 cUsersInList++;
635 }
636 }
637 else
638 VBoxServiceError("ConsoleKit: unable to lookup user name for uid=%RU32\n", uid);
639 }
640 else
641 AssertMsgFailed(("ConsoleKit: GetUnixUser returned a wrong argument type\n"));
642 }
643
644 if (pReplyUnixUser)
645 dbus_message_unref(pReplyUnixUser);
646 }
647 else
648 VBoxServiceError("ConsoleKit: unable to retrieve user for session '%s' (msg type=%d): %s",
649 *ppszCurSession, dbus_message_get_type(pMsgUnixUser),
650 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
651
652 if (pMsgUnixUser)
653 dbus_message_unref(pMsgUnixUser);
654 }
655
656 dbus_free_string_array(ppszSessions);
657 }
658 else
659 {
660 VBoxServiceError("ConsoleKit: unable to retrieve session parameters (msg type=%d): %s",
661 dbus_message_get_type(pMsgSessions),
662 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
663 }
664 dbus_message_unref(pReplySessions);
665 }
666
667 if (pMsgSessions)
668 {
669 dbus_message_unref(pMsgSessions);
670 pMsgSessions = NULL;
671 }
672 }
673 else
674 {
675 static int s_iBitchedAboutConsoleKit = 0;
676 if (s_iBitchedAboutConsoleKit++ < 3)
677 VBoxServiceError("Unable to invoke ConsoleKit (%d/3) -- maybe not installed / used? Error: %s\n",
678 s_iBitchedAboutConsoleKit,
679 dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available");
680 }
681
682 if (pMsgSessions)
683 dbus_message_unref(pMsgSessions);
684 }
685 else
686 {
687 static int s_iBitchedAboutDBus = 0;
688 if (s_iBitchedAboutDBus++ < 3)
689 VBoxServiceError("Unable to connect to system D-Bus (%d/3): %s\n", s_iBitchedAboutDBus,
690 pConnection && dbus_error_is_set(&dbErr) ? dbErr.message : "D-Bus not installed");
691 }
692
693 if ( pConnection
694 && dbus_error_is_set(&dbErr))
695 dbus_error_free(&dbErr);
696# endif /* RT_OS_LINUX */
697#endif /* VBOX_WITH_DBUS */
698
699 /** @todo Fedora/others: Handle systemd-loginctl. */
700
701 /* Calc the string length. */
702 size_t cchUserList = 0;
703 if (RT_SUCCESS(rc))
704 {
705 for (uint32_t i = 0; i < cUsersInList; i++)
706 cchUserList += (i != 0) + strlen(papszUsers[i]);
707 }
708
709 /* Build the user list. */
710 if (RT_SUCCESS(rc))
711 rc = RTStrAllocEx(&pszUserList, cchUserList + 1);
712 if (RT_SUCCESS(rc))
713 {
714 char *psz = pszUserList;
715 for (uint32_t i = 0; i < cUsersInList; i++)
716 {
717 if (i != 0)
718 *psz++ = ',';
719 size_t cch = strlen(papszUsers[i]);
720 memcpy(psz, papszUsers[i], cch);
721 psz += cch;
722 }
723 *psz = '\0';
724 }
725
726 /* Cleanup. */
727 for (uint32_t i = 0; i < cUsersInList; i++)
728 RTStrFree(papszUsers[i]);
729 RTMemFree(papszUsers);
730
731 endutxent(); /* Close utmpx file. */
732#endif
733 Assert(RT_FAILURE(rc) || cUsersInList == 0 || (pszUserList && *pszUserList));
734
735 /* If the user enumeration above failed, reset the user count to 0 except
736 * we didn't have enough memory anymore. In that case we want to preserve
737 * the previous user count in order to not confuse third party tools which
738 * rely on that count. */
739 if (RT_FAILURE(rc))
740 {
741 if (rc == VERR_NO_MEMORY)
742 {
743 static int s_iVMInfoBitchedOOM = 0;
744 if (s_iVMInfoBitchedOOM++ < 3)
745 VBoxServiceVerbose(0, "Warning: Not enough memory available to enumerate users! Keeping old value (%RU32)\n",
746 g_cVMInfoLoggedInUsers);
747 cUsersInList = g_cVMInfoLoggedInUsers;
748 }
749 else
750 cUsersInList = 0;
751 }
752 else /* Preserve logged in users count. */
753 g_cVMInfoLoggedInUsers = cUsersInList;
754
755 VBoxServiceVerbose(4, "cUsersInList=%RU32, pszUserList=%s, rc=%Rrc\n",
756 cUsersInList, pszUserList ? pszUserList : "<NULL>", rc);
757
758 if (pszUserList)
759 {
760 AssertMsg(cUsersInList, ("pszUserList contains users whereas cUsersInList is 0\n"));
761 rc = VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, "%s", pszUserList);
762 }
763 else
764 rc = VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, NULL);
765 if (RT_FAILURE(rc))
766 VBoxServiceError("Error writing logged in users list, rc=%Rrc\n", rc);
767
768 rc = VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers, "%RU32", cUsersInList);
769 if (RT_FAILURE(rc))
770 VBoxServiceError("Error writing logged in users count, rc=%Rrc\n", rc);
771
772 rc = VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers,
773 cUsersInList == 0 ? "true" : "false");
774 if (RT_FAILURE(rc))
775 VBoxServiceError("Error writing no logged in users beacon, rc=%Rrc\n", rc);
776
777 if (pszUserList)
778 RTStrFree(pszUserList);
779
780 VBoxServiceVerbose(4, "Writing users returned with rc=%Rrc\n", rc);
781 return rc;
782}
783
784
785/**
786 * Provide information about the guest network.
787 */
788static int vboxserviceVMInfoWriteNetwork(void)
789{
790 int rc = VINF_SUCCESS;
791 uint32_t cIfacesReport = 0;
792 char szPropPath[256];
793
794#ifdef RT_OS_WINDOWS
795 IP_ADAPTER_INFO *pAdpInfo = NULL;
796
797# ifndef TARGET_NT4
798 ULONG cbAdpInfo = sizeof(*pAdpInfo);
799 pAdpInfo = (IP_ADAPTER_INFO *)RTMemAlloc(cbAdpInfo);
800 if (!pAdpInfo)
801 {
802 VBoxServiceError("VMInfo/Network: Failed to allocate IP_ADAPTER_INFO\n");
803 return VERR_NO_MEMORY;
804 }
805 DWORD dwRet = GetAdaptersInfo(pAdpInfo, &cbAdpInfo);
806 if (dwRet == ERROR_BUFFER_OVERFLOW)
807 {
808 IP_ADAPTER_INFO *pAdpInfoNew = (IP_ADAPTER_INFO*)RTMemRealloc(pAdpInfo, cbAdpInfo);
809 if (pAdpInfoNew)
810 {
811 pAdpInfo = pAdpInfoNew;
812 dwRet = GetAdaptersInfo(pAdpInfo, &cbAdpInfo);
813 }
814 }
815 else if (dwRet == ERROR_NO_DATA)
816 {
817 VBoxServiceVerbose(3, "VMInfo/Network: No network adapters available\n");
818
819 /* If no network adapters available / present in the
820 * system we pretend success to not bail out too early. */
821 dwRet = ERROR_SUCCESS;
822 }
823
824 if (dwRet != ERROR_SUCCESS)
825 {
826 if (pAdpInfo)
827 RTMemFree(pAdpInfo);
828 VBoxServiceError("VMInfo/Network: Failed to get adapter info: Error %d\n", dwRet);
829 return RTErrConvertFromWin32(dwRet);
830 }
831# endif /* !TARGET_NT4 */
832
833 SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
834 if (sd == SOCKET_ERROR) /* Socket invalid. */
835 {
836 int wsaErr = WSAGetLastError();
837 /* Don't complain/bail out with an error if network stack is not up; can happen
838 * on NT4 due to start up when not connected shares dialogs pop up. */
839 if (WSAENETDOWN == wsaErr)
840 {
841 VBoxServiceVerbose(0, "VMInfo/Network: Network is not up yet.\n");
842 wsaErr = VINF_SUCCESS;
843 }
844 else
845 VBoxServiceError("VMInfo/Network: Failed to get a socket: Error %d\n", wsaErr);
846 if (pAdpInfo)
847 RTMemFree(pAdpInfo);
848 return RTErrConvertFromWin32(wsaErr);
849 }
850
851 INTERFACE_INFO InterfaceList[20] = {0};
852 unsigned long nBytesReturned = 0;
853 if (WSAIoctl(sd,
854 SIO_GET_INTERFACE_LIST,
855 0,
856 0,
857 &InterfaceList,
858 sizeof(InterfaceList),
859 &nBytesReturned,
860 0,
861 0) == SOCKET_ERROR)
862 {
863 VBoxServiceError("VMInfo/Network: Failed to WSAIoctl() on socket: Error: %d\n", WSAGetLastError());
864 if (pAdpInfo)
865 RTMemFree(pAdpInfo);
866 return RTErrConvertFromWin32(WSAGetLastError());
867 }
868 int cIfacesSystem = nBytesReturned / sizeof(INTERFACE_INFO);
869
870 /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
871 for (int i = 0; i < cIfacesSystem; ++i)
872 {
873 sockaddr_in *pAddress;
874 u_long nFlags = 0;
875 if (InterfaceList[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
876 continue;
877 nFlags = InterfaceList[i].iiFlags;
878 pAddress = (sockaddr_in *)&(InterfaceList[i].iiAddress);
879 Assert(pAddress);
880 char szIp[32];
881 RTStrPrintf(szIp, sizeof(szIp), "%s", inet_ntoa(pAddress->sin_addr));
882 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfacesReport);
883 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szIp);
884
885 pAddress = (sockaddr_in *) & (InterfaceList[i].iiBroadcastAddress);
886 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfacesReport);
887 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
888
889 pAddress = (sockaddr_in *)&(InterfaceList[i].iiNetmask);
890 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfacesReport);
891 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
892
893 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfacesReport);
894 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, nFlags & IFF_UP ? "Up" : "Down");
895
896# ifndef TARGET_NT4
897 IP_ADAPTER_INFO *pAdp;
898 for (pAdp = pAdpInfo; pAdp; pAdp = pAdp->Next)
899 if (!strcmp(pAdp->IpAddressList.IpAddress.String, szIp))
900 break;
901
902 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfacesReport);
903 if (pAdp)
904 {
905 char szMac[32];
906 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
907 pAdp->Address[0], pAdp->Address[1], pAdp->Address[2],
908 pAdp->Address[3], pAdp->Address[4], pAdp->Address[5]);
909 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
910 }
911 else
912 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, NULL);
913# endif /* !TARGET_NT4 */
914
915 cIfacesReport++;
916 }
917 if (pAdpInfo)
918 RTMemFree(pAdpInfo);
919 if (sd >= 0)
920 closesocket(sd);
921
922#elif defined(RT_OS_HAIKU)
923 /** @todo Haiku: implement network info. retreival */
924 return VERR_NOT_IMPLEMENTED;
925
926#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
927 struct ifaddrs *pIfHead = NULL;
928
929 /* Get all available interfaces */
930 rc = getifaddrs(&pIfHead);
931 if (rc < 0)
932 {
933 rc = RTErrConvertFromErrno(errno);
934 VBoxServiceError("VMInfo/Network: Failed to get all interfaces: Error %Rrc\n");
935 return rc;
936 }
937
938 /* Loop through all interfaces and set the data. */
939 for (struct ifaddrs *pIfCurr = pIfHead; pIfCurr; pIfCurr = pIfCurr->ifa_next)
940 {
941 /*
942 * Only AF_INET and no loopback interfaces
943 * @todo: IPv6 interfaces
944 */
945 if ( pIfCurr->ifa_addr->sa_family == AF_INET
946 && !(pIfCurr->ifa_flags & IFF_LOOPBACK))
947 {
948 char szInetAddr[NI_MAXHOST];
949
950 memset(szInetAddr, 0, NI_MAXHOST);
951 getnameinfo(pIfCurr->ifa_addr, sizeof(struct sockaddr_in),
952 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
953 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfacesReport);
954 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
955
956 memset(szInetAddr, 0, NI_MAXHOST);
957 getnameinfo(pIfCurr->ifa_broadaddr, sizeof(struct sockaddr_in),
958 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
959 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfacesReport);
960 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
961
962 memset(szInetAddr, 0, NI_MAXHOST);
963 getnameinfo(pIfCurr->ifa_netmask, sizeof(struct sockaddr_in),
964 szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
965 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfacesReport);
966 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr);
967
968 /* Search for the AF_LINK interface of the current AF_INET one and get the mac. */
969 for (struct ifaddrs *pIfLinkCurr = pIfHead; pIfLinkCurr; pIfLinkCurr = pIfLinkCurr->ifa_next)
970 {
971 if ( pIfLinkCurr->ifa_addr->sa_family == AF_LINK
972 && !strcmp(pIfCurr->ifa_name, pIfLinkCurr->ifa_name))
973 {
974 char szMac[32];
975 uint8_t *pu8Mac = NULL;
976 struct sockaddr_dl *pLinkAddress = (struct sockaddr_dl *)pIfLinkCurr->ifa_addr;
977
978 AssertPtr(pLinkAddress);
979 pu8Mac = (uint8_t *)LLADDR(pLinkAddress);
980 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
981 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
982 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfacesReport);
983 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
984 break;
985 }
986 }
987
988 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfacesReport);
989 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down");
990
991 cIfacesReport++;
992 }
993 }
994
995 /* Free allocated resources. */
996 freeifaddrs(pIfHead);
997
998#else /* !RT_OS_WINDOWS && !RT_OS_FREEBSD */
999 int sd = socket(AF_INET, SOCK_DGRAM, 0);
1000 if (sd < 0)
1001 {
1002 rc = RTErrConvertFromErrno(errno);
1003 VBoxServiceError("VMInfo/Network: Failed to get a socket: Error %Rrc\n", rc);
1004 return rc;
1005 }
1006
1007 ifconf ifcfg;
1008 char buffer[1024] = {0};
1009 ifcfg.ifc_len = sizeof(buffer);
1010 ifcfg.ifc_buf = buffer;
1011 if (ioctl(sd, SIOCGIFCONF, &ifcfg) < 0)
1012 {
1013 close(sd);
1014 rc = RTErrConvertFromErrno(errno);
1015 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFCONF) on socket: Error %Rrc\n", rc);
1016 return rc;
1017 }
1018
1019 ifreq* ifrequest = ifcfg.ifc_req;
1020 int cIfacesSystem = ifcfg.ifc_len / sizeof(ifreq);
1021
1022 for (int i = 0; i < cIfacesSystem; ++i)
1023 {
1024 sockaddr_in *pAddress;
1025 if (ioctl(sd, SIOCGIFFLAGS, &ifrequest[i]) < 0)
1026 {
1027 rc = RTErrConvertFromErrno(errno);
1028 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFFLAGS) on socket: Error %Rrc\n", rc);
1029 break;
1030 }
1031 if (ifrequest[i].ifr_flags & IFF_LOOPBACK) /* Skip the loopback device. */
1032 continue;
1033
1034 bool fIfUp = !!(ifrequest[i].ifr_flags & IFF_UP);
1035 pAddress = ((sockaddr_in *)&ifrequest[i].ifr_addr);
1036 Assert(pAddress);
1037 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfacesReport);
1038 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1039
1040 if (ioctl(sd, SIOCGIFBRDADDR, &ifrequest[i]) < 0)
1041 {
1042 rc = RTErrConvertFromErrno(errno);
1043 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %Rrc\n", rc);
1044 break;
1045 }
1046 pAddress = (sockaddr_in *)&ifrequest[i].ifr_broadaddr;
1047 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfacesReport);
1048 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1049
1050 if (ioctl(sd, SIOCGIFNETMASK, &ifrequest[i]) < 0)
1051 {
1052 rc = RTErrConvertFromErrno(errno);
1053 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFNETMASK) on socket: Error %Rrc\n", rc);
1054 break;
1055 }
1056# if defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
1057 pAddress = (sockaddr_in *)&ifrequest[i].ifr_addr;
1058# else
1059 pAddress = (sockaddr_in *)&ifrequest[i].ifr_netmask;
1060# endif
1061
1062 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfacesReport);
1063 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr));
1064
1065# if defined(RT_OS_SOLARIS)
1066 /*
1067 * "ifreq" is obsolete on Solaris. We use the recommended "lifreq".
1068 * We might fail if the interface has not been assigned an IP address.
1069 * That doesn't matter; as long as it's plumbed we can pick it up.
1070 * But, if it has not acquired an IP address we cannot obtain it's MAC
1071 * address this way, so we just use all zeros there.
1072 */
1073 RTMAC IfMac;
1074 RT_ZERO(IfMac);
1075 struct lifreq IfReq;
1076 RT_ZERO(IfReq);
1077 AssertCompile(sizeof(IfReq.lifr_name) >= sizeof(ifrequest[i].ifr_name));
1078 strncpy(IfReq.lifr_name, ifrequest[i].ifr_name, sizeof(ifrequest[i].ifr_name));
1079 if (ioctl(sd, SIOCGLIFADDR, &IfReq) >= 0)
1080 {
1081 struct arpreq ArpReq;
1082 RT_ZERO(ArpReq);
1083 memcpy(&ArpReq.arp_pa, &IfReq.lifr_addr, sizeof(struct sockaddr_in));
1084
1085 if (ioctl(sd, SIOCGARP, &ArpReq) >= 0)
1086 memcpy(&IfMac, ArpReq.arp_ha.sa_data, sizeof(IfMac));
1087 else
1088 {
1089 rc = RTErrConvertFromErrno(errno);
1090 VBoxServiceError("VMInfo/Network: failed to ioctl(SIOCGARP) on socket: Error %Rrc\n", rc);
1091 break;
1092 }
1093 }
1094 else
1095 {
1096 VBoxServiceVerbose(2, "VMInfo/Network: Interface %d has no assigned IP address, skipping ...\n", i);
1097 continue;
1098 }
1099# else
1100# ifndef RT_OS_OS2 /** @todo port this to OS/2 */
1101 if (ioctl(sd, SIOCGIFHWADDR, &ifrequest[i]) < 0)
1102 {
1103 rc = RTErrConvertFromErrno(errno);
1104 VBoxServiceError("VMInfo/Network: Failed to ioctl(SIOCGIFHWADDR) on socket: Error %Rrc\n", rc);
1105 break;
1106 }
1107# endif
1108# endif
1109
1110# ifndef RT_OS_OS2 /** @todo port this to OS/2 */
1111 char szMac[32];
1112# if defined(RT_OS_SOLARIS)
1113 uint8_t *pu8Mac = IfMac.au8;
1114# else
1115 uint8_t *pu8Mac = (uint8_t*)&ifrequest[i].ifr_hwaddr.sa_data[0]; /* @todo see above */
1116# endif
1117 RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X",
1118 pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]);
1119 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfacesReport);
1120 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac);
1121# endif /* !OS/2*/
1122
1123 RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfacesReport);
1124 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, fIfUp ? "Up" : "Down");
1125 cIfacesReport++;
1126 } /* For all interfaces */
1127
1128 close(sd);
1129 if (RT_FAILURE(rc))
1130 VBoxServiceError("VMInfo/Network: Network enumeration for interface %RU32 failed with error %Rrc\n", cIfacesReport, rc);
1131
1132#endif /* !RT_OS_WINDOWS */
1133
1134#if 0 /* Zapping not enabled yet, needs more testing first. */
1135 /*
1136 * Zap all stale network interface data if the former (saved) network ifaces count
1137 * is bigger than the current one.
1138 */
1139
1140 /* Get former count. */
1141 uint32_t cIfacesReportOld;
1142 rc = VBoxServiceReadPropUInt32(g_uVMInfoGuestPropSvcClientID, g_pszPropCacheValNetCount, &cIfacesReportOld,
1143 0 /* Min */, UINT32_MAX /* Max */);
1144 if ( RT_SUCCESS(rc)
1145 && cIfacesReportOld > cIfacesReport) /* Are some ifaces not around anymore? */
1146 {
1147 VBoxServiceVerbose(3, "VMInfo/Network: Stale interface data detected (%RU32 old vs. %RU32 current)\n",
1148 cIfacesReportOld, cIfacesReport);
1149
1150 uint32_t uIfaceDeleteIdx = cIfacesReport;
1151 do
1152 {
1153 VBoxServiceVerbose(3, "VMInfo/Network: Deleting stale data of interface %d ...\n", uIfaceDeleteIdx);
1154 rc = VBoxServicePropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%RU32", uIfaceDeleteIdx++);
1155 } while (RT_SUCCESS(rc));
1156 }
1157 else if ( RT_FAILURE(rc)
1158 && rc != VERR_NOT_FOUND)
1159 {
1160 VBoxServiceError("VMInfo/Network: Failed retrieving old network interfaces count with error %Rrc\n", rc);
1161 }
1162#endif
1163
1164 /*
1165 * This property is a beacon which is _always_ written, even if the network configuration
1166 * does not change. If this property is missing, the host assumes that all other GuestInfo
1167 * properties are no longer valid.
1168 */
1169 VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNetCount, "%RU32",
1170 cIfacesReport);
1171
1172 /* Don't fail here; just report everything we got. */
1173 return VINF_SUCCESS;
1174}
1175
1176
1177/** @copydoc VBOXSERVICE::pfnWorker */
1178DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown)
1179{
1180 int rc;
1181
1182 /*
1183 * Tell the control thread that it can continue
1184 * spawning services.
1185 */
1186 RTThreadUserSignal(RTThreadSelf());
1187
1188#ifdef RT_OS_WINDOWS
1189 /* Required for network information (must be called per thread). */
1190 WSADATA wsaData;
1191 if (WSAStartup(MAKEWORD(2, 2), &wsaData))
1192 VBoxServiceError("VMInfo/Network: WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(WSAGetLastError()));
1193#endif /* RT_OS_WINDOWS */
1194
1195 /*
1196 * Write the fixed properties first.
1197 */
1198 vboxserviceVMInfoWriteFixedProperties();
1199
1200 /*
1201 * Now enter the loop retrieving runtime data continuously.
1202 */
1203 for (;;)
1204 {
1205 rc = vboxserviceVMInfoWriteUsers();
1206 if (RT_FAILURE(rc))
1207 break;
1208
1209 rc = vboxserviceVMInfoWriteNetwork();
1210 if (RT_FAILURE(rc))
1211 break;
1212
1213 /* Whether to wait for event semaphore or not. */
1214 bool fWait = true;
1215
1216 /* Check for location awareness. This most likely only
1217 * works with VBox (latest) 4.1 and up. */
1218
1219 /* Check for new connection. */
1220 char *pszLAClientID = NULL;
1221 int rc2 = VBoxServiceReadHostProp(g_uVMInfoGuestPropSvcClientID, g_pszLAActiveClient, true /* Read only */,
1222 &pszLAClientID, NULL /* Flags */, NULL /* Timestamp */);
1223 if (RT_SUCCESS(rc2))
1224 {
1225 AssertPtr(pszLAClientID);
1226 if (RTStrICmp(pszLAClientID, "0")) /* Is a client connected? */
1227 {
1228 uint32_t uLAClientID = RTStrToInt32(pszLAClientID);
1229 uint64_t uLAClientAttachedTS;
1230
1231 /* Peek at "Attach" value to figure out if hotdesking happened. */
1232 char *pszAttach = NULL;
1233 rc2 = vboxServiceGetLAClientValue(uLAClientID, "Attach", &pszAttach,
1234 &uLAClientAttachedTS);
1235
1236 if ( RT_SUCCESS(rc2)
1237 && ( !g_LAClientAttachedTS
1238 || (g_LAClientAttachedTS != uLAClientAttachedTS)))
1239 {
1240 vboxServiceFreeLAClientInfo(&g_LAClientInfo);
1241
1242 /* Note: There is a race between setting the guest properties by the host and getting them by
1243 * the guest. */
1244 rc2 = vboxServiceGetLAClientInfo(uLAClientID, &g_LAClientInfo);
1245 if (RT_SUCCESS(rc2))
1246 {
1247 VBoxServiceVerbose(1, "VRDP: Hotdesk client %s with ID=%RU32, Name=%s, Domain=%s\n",
1248 /* If g_LAClientAttachedTS is 0 this means there already was an active
1249 * hotdesk session when VBoxService started. */
1250 !g_LAClientAttachedTS ? "already active" : g_LAClientInfo.fAttached ? "connected" : "disconnected",
1251 uLAClientID, g_LAClientInfo.pszName, g_LAClientInfo.pszDomain);
1252
1253 g_LAClientAttachedTS = g_LAClientInfo.uAttachedTS;
1254
1255 /* Don't wait for event semaphore below anymore because we now know that the client
1256 * changed. This means we need to iterate all VM information again immediately. */
1257 fWait = false;
1258 }
1259 else
1260 {
1261 static int s_iBitchedAboutLAClientInfo = 0;
1262 if (s_iBitchedAboutLAClientInfo++ < 10)
1263 VBoxServiceError("Error getting active location awareness client info, rc=%Rrc\n", rc2);
1264 }
1265 }
1266 else if (RT_FAILURE(rc2))
1267 VBoxServiceError("Error getting attached value of location awareness client %RU32, rc=%Rrc\n",
1268 uLAClientID, rc2);
1269 if (pszAttach)
1270 RTStrFree(pszAttach);
1271 }
1272 else
1273 {
1274 VBoxServiceVerbose(1, "VRDP: UTTSC disconnected from VRDP server\n");
1275 vboxServiceFreeLAClientInfo(&g_LAClientInfo);
1276 }
1277
1278 RTStrFree(pszLAClientID);
1279 }
1280 else
1281 {
1282 static int s_iBitchedAboutLAClient = 0;
1283 if ( (rc2 != VERR_NOT_FOUND) /* No location awareness installed, skip. */
1284 && s_iBitchedAboutLAClient++ < 3)
1285 VBoxServiceError("VRDP: Querying connected location awareness client failed with rc=%Rrc\n", rc2);
1286 }
1287
1288 VBoxServiceVerbose(3, "VRDP: Handling location awareness done\n");
1289
1290 /*
1291 * Flush all properties if we were restored.
1292 */
1293 uint64_t idNewSession = g_idVMInfoSession;
1294 VbglR3GetSessionId(&idNewSession);
1295 if (idNewSession != g_idVMInfoSession)
1296 {
1297 VBoxServiceVerbose(3, "The VM session ID changed, flushing all properties\n");
1298 vboxserviceVMInfoWriteFixedProperties();
1299 VBoxServicePropCacheFlush(&g_VMInfoPropCache);
1300 g_idVMInfoSession = idNewSession;
1301 }
1302
1303 /*
1304 * Block for a while.
1305 *
1306 * The event semaphore takes care of ignoring interruptions and it
1307 * allows us to implement service wakeup later.
1308 */
1309 if (*pfShutdown)
1310 break;
1311 if (fWait)
1312 rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval);
1313 if (*pfShutdown)
1314 break;
1315 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
1316 {
1317 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
1318 rc = rc2;
1319 break;
1320 }
1321 else if (RT_LIKELY(RT_SUCCESS(rc2)))
1322 {
1323 /* Reset event semaphore if it got triggered. */
1324 rc2 = RTSemEventMultiReset(g_hVMInfoEvent);
1325 if (RT_FAILURE(rc2))
1326 rc2 = VBoxServiceError("RTSemEventMultiReset failed; rc2=%Rrc\n", rc2);
1327 }
1328 }
1329
1330#ifdef RT_OS_WINDOWS
1331 WSACleanup();
1332#endif
1333
1334 return rc;
1335}
1336
1337
1338/** @copydoc VBOXSERVICE::pfnStop */
1339static DECLCALLBACK(void) VBoxServiceVMInfoStop(void)
1340{
1341 RTSemEventMultiSignal(g_hVMInfoEvent);
1342}
1343
1344
1345/** @copydoc VBOXSERVICE::pfnTerm */
1346static DECLCALLBACK(void) VBoxServiceVMInfoTerm(void)
1347{
1348 if (g_hVMInfoEvent != NIL_RTSEMEVENTMULTI)
1349 {
1350 /** @todo temporary solution: Zap all values which are not valid
1351 * anymore when VM goes down (reboot/shutdown ). Needs to
1352 * be replaced with "temporary properties" later.
1353 *
1354 * One idea is to introduce a (HGCM-)session guest property
1355 * flag meaning that a guest property is only valid as long
1356 * as the HGCM session isn't closed (e.g. guest application
1357 * terminates). [don't remove till implemented]
1358 */
1359 /** @todo r=bird: Drop the VbglR3GuestPropDelSet call here and use the cache
1360 * since it remembers what we've written. */
1361 /* Delete the "../Net" branch. */
1362 const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
1363 int rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
1364
1365 /* Destroy LA client info. */
1366 vboxServiceFreeLAClientInfo(&g_LAClientInfo);
1367
1368 /* Destroy property cache. */
1369 VBoxServicePropCacheDestroy(&g_VMInfoPropCache);
1370
1371 /* Disconnect from guest properties service. */
1372 rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID);
1373 if (RT_FAILURE(rc))
1374 VBoxServiceError("Failed to disconnect from guest property service! Error: %Rrc\n", rc);
1375 g_uVMInfoGuestPropSvcClientID = 0;
1376
1377 RTSemEventMultiDestroy(g_hVMInfoEvent);
1378 g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
1379 }
1380}
1381
1382
1383/**
1384 * The 'vminfo' service description.
1385 */
1386VBOXSERVICE g_VMInfo =
1387{
1388 /* pszName. */
1389 "vminfo",
1390 /* pszDescription. */
1391 "Virtual Machine Information",
1392 /* pszUsage. */
1393 " [--vminfo-interval <ms>]"
1394 ,
1395 /* pszOptions. */
1396 " --vminfo-interval Specifies the interval at which to retrieve the\n"
1397 " VM information. The default is 10000 ms.\n"
1398 ,
1399 /* methods */
1400 VBoxServiceVMInfoPreInit,
1401 VBoxServiceVMInfoOption,
1402 VBoxServiceVMInfoInit,
1403 VBoxServiceVMInfoWorker,
1404 VBoxServiceVMInfoStop,
1405 VBoxServiceVMInfoTerm
1406};
1407
Note: See TracBrowser for help on using the repository browser.

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