VirtualBox

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

Last change on this file since 21528 was 21528, checked in by vboxsync, 15 years ago

Additions: fix updating NoLoggedInUsers when VBoxService is started

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 KB
Line 
1/* $Id: VBoxServiceVMInfo.cpp 21528 2009-07-13 11:52:24Z vboxsync $ */
2/** @file
3 * VBoxVMInfo - Virtual machine (guest) information for the host.
4 */
5
6/*
7 * Copyright (C) 2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#ifdef RT_OS_WINDOWS
28#include <winsock2.h>
29#include <ws2tcpip.h>
30#include <windows.h>
31#include <Ntsecapi.h>
32#else
33# define __STDC_LIMIT_MACROS
34# include <arpa/inet.h>
35# include <errno.h>
36# include <net/if.h>
37# include <netinet/in.h>
38# include <sys/ioctl.h>
39# include <sys/socket.h>
40# include <unistd.h>
41# include <utmp.h>
42# ifdef RT_OS_SOLARIS
43# include <sys/sockio.h>
44# endif
45#endif
46
47#include <iprt/mem.h>
48#include <iprt/thread.h>
49#include <iprt/string.h>
50#include <iprt/semaphore.h>
51#include <iprt/system.h>
52#include <iprt/time.h>
53#include <iprt/assert.h>
54#include <VBox/VBoxGuestLib.h>
55#include "VBoxServiceInternal.h"
56#include "VBoxServiceUtils.h"
57
58
59/*******************************************************************************
60* Global Variables *
61*******************************************************************************/
62/** The vminfo interval (millseconds). */
63uint32_t g_VMInfoInterval = 0;
64/** The semaphore we're blocking on. */
65static RTSEMEVENTMULTI g_VMInfoEvent = NIL_RTSEMEVENTMULTI;
66/** The guest property service client ID. */
67static uint32_t g_VMInfoGuestPropSvcClientID = 0;
68/** Number of logged in users in OS. */
69static uint32_t g_VMInfoLoggedInUsers = UINT32_MAX;
70#ifdef RT_OS_WINDOWS
71/** Function prototypes for dynamic loading. */
72fnWTSGetActiveConsoleSessionId g_pfnWTSGetActiveConsoleSessionId = NULL;
73/** External functions. */
74extern int VboxServiceWinGetAddsVersion(uint32_t uiClientID);
75extern int VboxServiceWinGetComponentVersions(uint32_t uiClientID);
76#endif
77
78
79/** @copydoc VBOXSERVICE::pfnPreInit */
80static DECLCALLBACK(int) VBoxServiceVMInfoPreInit(void)
81{
82 return VINF_SUCCESS;
83}
84
85
86/** @copydoc VBOXSERVICE::pfnOption */
87static DECLCALLBACK(int) VBoxServiceVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi)
88{
89 int rc = -1;
90 if (ppszShort)
91 /* no short options */;
92 else if (!strcmp(argv[*pi], "--vminfo-interval"))
93 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
94 &g_VMInfoInterval, 1, UINT32_MAX - 1);
95 return rc;
96}
97
98
99/** @copydoc VBOXSERVICE::pfnInit */
100static DECLCALLBACK(int) VBoxServiceVMInfoInit(void)
101{
102 /*
103 * If not specified, find the right interval default.
104 * Then create the event sem to block on.
105 */
106 if (!g_VMInfoInterval)
107 g_VMInfoInterval = g_DefaultInterval * 1000;
108 if (!g_VMInfoInterval)
109 g_VMInfoInterval = 10 * 1000;
110
111 int rc = RTSemEventMultiCreate(&g_VMInfoEvent);
112 AssertRCReturn(rc, rc);
113
114#ifdef RT_OS_WINDOWS
115 /* Get function pointers. */
116 HMODULE hKernel32 = LoadLibrary(_T("kernel32"));
117 if (NULL != hKernel32)
118 {
119 g_pfnWTSGetActiveConsoleSessionId = (fnWTSGetActiveConsoleSessionId)GetProcAddress(hKernel32, "WTSGetActiveConsoleSessionId");
120 FreeLibrary(hKernel32);
121 }
122#endif
123
124 rc = VbglR3GuestPropConnect(&g_VMInfoGuestPropSvcClientID);
125 if (RT_SUCCESS(rc))
126 VBoxServiceVerbose(3, "Property Service Client ID: %#x\n", g_VMInfoGuestPropSvcClientID);
127 else
128 {
129 VBoxServiceError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
130 RTSemEventMultiDestroy(g_VMInfoEvent);
131 g_VMInfoEvent = NIL_RTSEMEVENTMULTI;
132 }
133
134 return rc;
135}
136
137
138/** @copydoc VBOXSERVICE::pfnWorker */
139DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown)
140{
141 int rc = VINF_SUCCESS;
142
143 /*
144 * Tell the control thread that it can continue
145 * spawning services.
146 */
147 RTThreadUserSignal(RTThreadSelf());
148
149#ifdef RT_OS_WINDOWS
150 /* Required for network information (must be called per thread). */
151 WSADATA wsaData;
152 if (WSAStartup(MAKEWORD(2, 2), &wsaData)) {
153 VBoxServiceError("WSAStartup failed! Error: %Rrc\n", RTErrConvertFromWin32(WSAGetLastError()));
154 }
155#endif /* !RT_OS_WINDOWS */
156
157 /* First get information that won't change while the OS is running. */
158 char szInfo[256] = {0};
159 rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szInfo, sizeof(szInfo));
160 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/Product", szInfo);
161
162 rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szInfo, sizeof(szInfo));
163 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/Release", szInfo);
164
165 rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szInfo, sizeof(szInfo));
166 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/Version", szInfo);
167
168 rc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szInfo, sizeof(szInfo));
169 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/ServicePack", szInfo);
170
171 /* Retrieve version information about Guest Additions and installed files (components). */
172#ifdef RT_OS_WINDOWS
173 rc = VboxServiceWinGetAddsVersion(g_VMInfoGuestPropSvcClientID);
174 rc = VboxServiceWinGetComponentVersions(g_VMInfoGuestPropSvcClientID);
175#else
176 /** @todo */
177#endif
178
179 /* Now enter the loop retrieving runtime data continuously. */
180 unsigned cErrors = 0;
181 for (;;)
182 {
183 /* Enumerate logged in users. */
184 uint32_t uiUserCount = 0;
185 char szUserList[4096] = {0};
186
187#ifdef RT_OS_WINDOWS
188 #ifndef TARGET_NT4
189 PLUID pSessions = NULL;
190 ULONG ulCount = 0;
191 NTSTATUS r = 0;
192
193 char* pszTemp = NULL;
194
195 /* This function can report stale or orphaned interactive logon sessions of already logged
196 off users (especially in Windows 2000). */
197 r = ::LsaEnumerateLogonSessions(&ulCount, &pSessions);
198 VBoxServiceVerbose(3, "Users: Found %ld users.\n", ulCount);
199
200 if (r != STATUS_SUCCESS)
201 {
202 VBoxServiceError("LsaEnumerate failed %lu\n", LsaNtStatusToWinError(r));
203 return 1;
204 }
205
206 PLUID pLuid = NULL;
207 DWORD dwNumOfProcLUIDs = VboxServiceVMInfoWinGetLUIDsFromProcesses(&pLuid);
208
209 VBOXSERVICEVMINFOUSER userInfo;
210 ZeroMemory (&userInfo, sizeof(VBOXSERVICEVMINFOUSER));
211
212 for (int i = 0; i<(int)ulCount; i++)
213 {
214 if (VboxServiceVMInfoWinIsLoggedIn(&userInfo, &pSessions[i], pLuid, dwNumOfProcLUIDs))
215 {
216 if (uiUserCount > 0)
217 strcat (szUserList, ",");
218
219 uiUserCount++;
220
221 RTUtf16ToUtf8(userInfo.szUser, &pszTemp);
222 strcat(szUserList, pszTemp);
223 RTMemFree(pszTemp);
224 }
225 }
226
227 if (NULL != pLuid)
228 ::LocalFree (pLuid);
229
230 ::LsaFreeReturnBuffer(pSessions);
231 #endif /* TARGET_NT4 */
232#else
233 utmp* ut_user;
234 rc = utmpname(UTMP_FILE);
235 if (rc != 0)
236 {
237 VBoxServiceError("Could not set UTMP file! Error: %ld", errno);
238 }
239 setutent();
240 while ((ut_user=getutent()))
241 {
242 /* Make sure we don't add user names which are not
243 * part of type USER_PROCESS and don't add same users twice. */
244 if ( (ut_user->ut_type == USER_PROCESS)
245 && (strstr(szUserList, ut_user->ut_user) == NULL))
246 {
247 /** @todo Do we really want to filter out double user names? (Same user logged in twice) */
248 if (uiUserCount > 0)
249 strcat(szUserList, ",");
250 strcat(szUserList, ut_user->ut_user);
251 uiUserCount++;
252 }
253 }
254 endutent();
255#endif /* !RT_OS_WINDOWS */
256
257 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/LoggedInUsersList", (uiUserCount > 0) ? szUserList : NULL);
258 VboxServiceWritePropInt(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/LoggedInUsers", uiUserCount);
259 if (g_VMInfoLoggedInUsers != uiUserCount || g_VMInfoLoggedInUsers == UINT32_MAX)
260 {
261 /* Update this property ONLY if there is a real change from no users to
262 * users or vice versa. The only exception is that the initialization
263 * forces an update, but only once. This ensures consistent property
264 * settings even if the VM aborted previously. */
265 if (uiUserCount == 0)
266 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/NoLoggedInUsers", "true");
267 else if (g_VMInfoLoggedInUsers == 0)
268 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/NoLoggedInUsers", "false");
269 }
270 g_VMInfoLoggedInUsers = uiUserCount;
271
272 /* Get network configuration. */
273 /** @todo Throw this code into a separate function/module? */
274 int nNumInterfaces = 0;
275#ifdef RT_OS_WINDOWS
276 SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
277 if (sd == SOCKET_ERROR) /* Socket invalid. */
278 {
279 VBoxServiceError("Failed to get a socket: Error %d\n", WSAGetLastError());
280 return -1;
281 }
282
283 INTERFACE_INFO InterfaceList[20] = {0};
284 unsigned long nBytesReturned = 0;
285 if (WSAIoctl(sd,
286 SIO_GET_INTERFACE_LIST,
287 0,
288 0,
289 &InterfaceList,
290 sizeof(InterfaceList),
291 &nBytesReturned,
292 0,
293 0) == SOCKET_ERROR)
294 {
295 VBoxServiceError("Failed to WSAIoctl() on socket: Error: %d\n", WSAGetLastError());
296 return -1;
297 }
298 nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
299#else
300 int sd = socket(AF_INET, SOCK_DGRAM, 0);
301 if (sd < 0) /* Socket invalid. */
302 {
303 VBoxServiceError("Failed to get a socket: Error %d\n", errno);
304 return -1;
305 }
306
307 ifconf ifcfg;
308 char buffer[1024] = {0};
309 ifcfg.ifc_len = sizeof(buffer);
310 ifcfg.ifc_buf = buffer;
311 if (ioctl(sd, SIOCGIFCONF, &ifcfg) < 0)
312 {
313 VBoxServiceError("Failed to ioctl(SIOCGIFCONF) on socket: Error %d\n", errno);
314 return -1;
315 }
316
317 ifreq* ifrequest = ifcfg.ifc_req;
318 ifreq *ifreqitem = NULL;
319 nNumInterfaces = ifcfg.ifc_len / sizeof(ifreq);
320#endif
321 char szPropPath [FILENAME_MAX] = {0};
322 char szTemp [FILENAME_MAX] = {0};
323 int iCurIface = 0;
324
325 RTStrPrintf(szPropPath, sizeof(szPropPath), "GuestInfo/Net/Count");
326 VboxServiceWritePropInt(g_VMInfoGuestPropSvcClientID, szPropPath, (nNumInterfaces > 1 ? nNumInterfaces-1 : 0));
327
328 /** @todo Use GetAdaptersInfo() and GetAdapterAddresses (IPv4 + IPv6) for more information. */
329 for (int i = 0; i < nNumInterfaces; ++i)
330 {
331 sockaddr_in *pAddress;
332 u_long nFlags = 0;
333#ifdef RT_OS_WINDOWS
334 if (InterfaceList[i].iiFlags & IFF_LOOPBACK) /* Skip loopback device. */
335 continue;
336 nFlags = InterfaceList[i].iiFlags;
337 pAddress = (sockaddr_in *)&(InterfaceList[i].iiAddress);
338#else
339 if (ioctl(sd, SIOCGIFFLAGS, &ifrequest[i]) < 0)
340 {
341 VBoxServiceError("Failed to ioctl(SIOCGIFFLAGS) on socket: Error %d\n", errno);
342 return -1;
343 }
344 if (ifrequest[i].ifr_flags & IFF_LOOPBACK) /* Skip loopback device. */
345 continue;
346 nFlags = ifrequest[i].ifr_flags;
347 pAddress = ((sockaddr_in *)&ifrequest[i].ifr_addr);
348#endif
349 Assert(pAddress);
350 RTStrPrintf(szPropPath, sizeof(szPropPath), "GuestInfo/Net/%d/V4/IP", iCurIface);
351 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, szPropPath, inet_ntoa(pAddress->sin_addr));
352
353#ifdef RT_OS_WINDOWS
354 pAddress = (sockaddr_in *) & (InterfaceList[i].iiBroadcastAddress);
355#else
356 if (ioctl(sd, SIOCGIFBRDADDR, &ifrequest[i]) < 0)
357 {
358 VBoxServiceError("Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %d\n", errno);
359 return -1;
360 }
361 pAddress = (sockaddr_in *)&ifrequest[i].ifr_broadaddr;
362#endif
363 RTStrPrintf(szPropPath, sizeof(szPropPath), "GuestInfo/Net/%d/V4/Broadcast", iCurIface);
364 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, szPropPath, inet_ntoa(pAddress->sin_addr));
365
366#ifdef RT_OS_WINDOWS
367 pAddress = (sockaddr_in *)&(InterfaceList[i].iiNetmask);
368#else
369 if (ioctl(sd, SIOCGIFNETMASK, &ifrequest[i]) < 0)
370 {
371 VBoxServiceError("Failed to ioctl(SIOCGIFBRDADDR) on socket: Error %d\n", errno);
372 return -1;
373 }
374 #ifdef RT_OS_SOLARIS
375 pAddress = (sockaddr_in *)&ifrequest[i].ifr_addr;
376 #else
377 pAddress = (sockaddr_in *)&ifrequest[i].ifr_netmask;
378 #endif
379
380#endif
381 RTStrPrintf(szPropPath, sizeof(szPropPath), "GuestInfo/Net/%d/V4/Netmask", iCurIface);
382 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, szPropPath, inet_ntoa(pAddress->sin_addr));
383
384 if (nFlags & IFF_UP)
385 RTStrPrintf(szTemp, sizeof(szTemp), "Up");
386 else
387 RTStrPrintf(szTemp, sizeof(szTemp), "Down");
388
389 RTStrPrintf(szPropPath, sizeof(szPropPath), "GuestInfo/Net/%d/Status", iCurIface);
390 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, szPropPath, szTemp);
391
392 iCurIface++;
393 }
394#ifdef RT_OS_WINDOWS
395 if (sd) closesocket(sd);
396#else
397 if (sd) close(sd);
398#endif /* !RT_OS_WINDOWS */
399
400 /*
401 * Block for a while.
402 *
403 * The event semaphore takes care of ignoring interruptions and it
404 * allows us to implement service wakeup later.
405 */
406 if (*pfShutdown)
407 break;
408 int rc2 = RTSemEventMultiWait(g_VMInfoEvent, g_VMInfoInterval);
409 if (*pfShutdown)
410 break;
411 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
412 {
413 VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
414 rc = rc2;
415 break;
416 }
417 }
418
419#ifdef RT_OS_WINDOWS
420 WSACleanup();
421#endif /* !RT_OS_WINDOWS */
422
423 RTSemEventMultiDestroy(g_VMInfoEvent);
424 g_VMInfoEvent = NIL_RTSEMEVENTMULTI;
425 return rc;
426}
427
428
429/** @copydoc VBOXSERVICE::pfnStop */
430static DECLCALLBACK(void) VBoxServiceVMInfoStop(void)
431{
432 RTSemEventMultiSignal(g_VMInfoEvent);
433}
434
435
436/** @copydoc VBOXSERVICE::pfnTerm */
437static DECLCALLBACK(void) VBoxServiceVMInfoTerm(void)
438{
439 int rc;
440
441 if (g_VMInfoEvent != NIL_RTSEMEVENTMULTI)
442 {
443 /** @todo temporary solution: Zap all values which are not valid
444 * anymore when VM goes down (reboot/shutdown ). Needs to
445 * be replaced with "temporary properties" later.
446 *
447 * @todo r=bird: This code isn't called on non-Windows systems. We need
448 * a more formal way of shutting down the service for that to work.
449 */
450 rc = VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/LoggedInUsersList", NULL);
451 rc = VboxServiceWritePropInt(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/LoggedInUsers", 0);
452 if (g_VMInfoLoggedInUsers > 0)
453 VboxServiceWriteProp(g_VMInfoGuestPropSvcClientID, "GuestInfo/OS/NoLoggedInUsers", "true");
454
455 const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" };
456 rc = VbglR3GuestPropDelSet(g_VMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat));
457 rc = VboxServiceWritePropInt(g_VMInfoGuestPropSvcClientID, "GuestInfo/Net/Count", 0);
458
459 /* Disconnect from guest properties service. */
460 rc = VbglR3GuestPropDisconnect(g_VMInfoGuestPropSvcClientID);
461 if (RT_FAILURE(rc))
462 VBoxServiceError("Failed to disconnect from guest property service! Error: %Rrc\n", rc);
463 g_VMInfoGuestPropSvcClientID = 0;
464
465
466 RTSemEventMultiDestroy(g_VMInfoEvent);
467 g_VMInfoEvent = NIL_RTSEMEVENTMULTI;
468 }
469}
470
471
472/**
473 * The 'vminfo' service description.
474 */
475VBOXSERVICE g_VMInfo =
476{
477 /* pszName. */
478 "vminfo",
479 /* pszDescription. */
480 "Virtual Machine Information",
481 /* pszUsage. */
482 "[--vminfo-interval <ms>]"
483 ,
484 /* pszOptions. */
485 " --vminfo-interval Specifies the interval at which to retrieve the\n"
486 " VM information. The default is 10000 ms.\n"
487 ,
488 /* methods */
489 VBoxServiceVMInfoPreInit,
490 VBoxServiceVMInfoOption,
491 VBoxServiceVMInfoInit,
492 VBoxServiceVMInfoWorker,
493 VBoxServiceVMInfoStop,
494 VBoxServiceVMInfoTerm
495};
496
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