VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/win/svcmain.cpp@ 51441

Last change on this file since 51441 was 49938, checked in by vboxsync, 11 years ago

Main/src-server/win/HostPowerWin.cpp: fix long standing bug which caused VBoxSVC to hang around for 5 seconds as the thread termination notification wasn't posted properly
Main/src-server/win/svcmain.cpp: reduced "just in case" sleep, shouldn't be necessary at all, when threads using COM are present the server shouldn't ever get to this place
Runtime/common/log/log.cpp: if there are sharing problems with the log file to be opened, wait for up to 10 seconds, maybe the previous user goes away (helps with VBoxSVC release log which can cause trouble, when the previous process is not quite terminated and some API client triggers the launch of a new one), effectively Windows only

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
Line 
1/** @file
2 *
3 * SVCMAIN - COM out-of-proc server main entry
4 */
5
6/*
7 * Copyright (C) 2004-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#include <Windows.h>
19#include <stdio.h>
20#include <stdlib.h>
21
22#include "VBox/com/defs.h"
23
24#include "VBox/com/com.h"
25
26#include "VBox/com/VirtualBox.h"
27
28#include "VirtualBoxImpl.h"
29#include "Logging.h"
30
31#include "svchlp.h"
32
33#include <VBox/err.h>
34#include <iprt/buildconfig.h>
35#include <iprt/initterm.h>
36#include <iprt/string.h>
37#include <iprt/uni.h>
38#include <iprt/path.h>
39#include <iprt/getopt.h>
40#include <iprt/message.h>
41
42#include <atlbase.h>
43#include <atlcom.h>
44
45#define _ATL_FREE_THREADED
46
47class CExeModule : public CComModule
48{
49public:
50 LONG Unlock();
51 DWORD dwThreadID;
52 HANDLE hEventShutdown;
53 void MonitorShutdown();
54 bool StartMonitor();
55 bool bActivity;
56};
57
58const DWORD dwTimeOut = 5000; /* time for EXE to be idle before shutting down */
59const DWORD dwPause = 100; /* time to wait for threads to finish up */
60
61/* Passed to CreateThread to monitor the shutdown event */
62static DWORD WINAPI MonitorProc(void* pv)
63{
64 CExeModule* p = (CExeModule*)pv;
65 p->MonitorShutdown();
66 return 0;
67}
68
69LONG CExeModule::Unlock()
70{
71 LONG l = CComModule::Unlock();
72 if (l == 0)
73 {
74 bActivity = true;
75 SetEvent(hEventShutdown); /* tell monitor that we transitioned to zero */
76 }
77 return l;
78}
79
80/* Monitors the shutdown event */
81void CExeModule::MonitorShutdown()
82{
83 while (1)
84 {
85 WaitForSingleObject(hEventShutdown, INFINITE);
86 DWORD dwWait=0;
87 do
88 {
89 bActivity = false;
90 dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
91 } while (dwWait == WAIT_OBJECT_0);
92 /* timed out */
93 if (!bActivity && m_nLockCnt == 0) /* if no activity let's really bail */
94 {
95#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
96 CoSuspendClassObjects();
97 if (!bActivity && m_nLockCnt == 0)
98#endif
99 break;
100 }
101 }
102 CloseHandle(hEventShutdown);
103 PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
104}
105
106bool CExeModule::StartMonitor()
107{
108 hEventShutdown = CreateEvent(NULL, false, false, NULL);
109 if (hEventShutdown == NULL)
110 return false;
111 DWORD dwThreadID;
112 HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &dwThreadID);
113 return (h != NULL);
114}
115
116CExeModule _Module;
117
118BEGIN_OBJECT_MAP(ObjectMap)
119 OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox)
120END_OBJECT_MAP()
121
122
123LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2)
124{
125 while (p1 != NULL && *p1 != NULL)
126 {
127 LPCTSTR p = p2;
128 while (p != NULL && *p != NULL)
129 {
130 if (*p1 == *p)
131 return CharNext(p1);
132 p = CharNext(p);
133 }
134 p1 = CharNext(p1);
135 }
136 return NULL;
137}
138
139static int WordCmpI(LPCTSTR psz1, LPCTSTR psz2) throw()
140{
141 TCHAR c1 = (TCHAR)CharUpper((LPTSTR)*psz1);
142 TCHAR c2 = (TCHAR)CharUpper((LPTSTR)*psz2);
143 while (c1 != NULL && c1 == c2 && c1 != ' ' && c1 != '\t')
144 {
145 psz1 = CharNext(psz1);
146 psz2 = CharNext(psz2);
147 c1 = (TCHAR)CharUpper((LPTSTR)*psz1);
148 c2 = (TCHAR)CharUpper((LPTSTR)*psz2);
149 }
150 if ((c1 == NULL || c1 == ' ' || c1 == '\t') && (c2 == NULL || c2 == ' ' || c2 == '\t'))
151 return 0;
152
153 return (c1 < c2) ? -1 : 1;
154}
155
156/////////////////////////////////////////////////////////////////////////////
157//
158int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
159{
160 LPCTSTR lpCmdLine = GetCommandLine(); /* this line necessary for _ATL_MIN_CRT */
161
162 /*
163 * Need to parse the command line before initializing the VBox runtime.
164 */
165 TCHAR szTokens[] = _T("-/");
166 LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens);
167 while (lpszToken != NULL)
168 {
169 if (WordCmpI(lpszToken, _T("Embedding")) == 0)
170 {
171 /* %HOMEDRIVE%%HOMEPATH% */
172 wchar_t wszHome[RTPATH_MAX];
173 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
174 if (cEnv && cEnv < RTPATH_MAX)
175 {
176 DWORD cwc = cEnv; /* doesn't include NUL */
177 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
178 if (cEnv && cEnv < RTPATH_MAX - cwc)
179 {
180 /* If this fails there is nothing we can do. Ignore. */
181 SetCurrentDirectory(wszHome);
182 }
183 }
184 }
185
186 lpszToken = FindOneOf(lpszToken, szTokens);
187 }
188
189 /*
190 * Initialize the VBox runtime without loading
191 * the support driver.
192 */
193 int argc = __argc;
194 char **argv = __argv;
195 RTR3InitExe(argc, &argv, 0);
196
197 /* Note that all options are given lowercase/camel case/uppercase to
198 * approximate case insensitive matching, which RTGetOpt doesn't offer. */
199 static const RTGETOPTDEF s_aOptions[] =
200 {
201 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
202 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
203 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
204 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
205 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
206 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
207 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
208 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
209 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
210 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
211 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
212 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
213 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
214 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
215 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
216 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
217 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
218 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
219 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
220 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
221 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
222 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
223 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
224 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
225 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
226 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
227 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
228 };
229
230 bool fRun = true;
231 bool fRegister = false;
232 bool fUnregister = false;
233 const char *pszPipeName = NULL;
234 const char *pszLogFile = NULL;
235 uint32_t cHistory = 10; // enable log rotation, 10 files
236 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
237 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
238
239 RTGETOPTSTATE GetOptState;
240 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
241 AssertRC(vrc);
242
243 RTGETOPTUNION ValueUnion;
244 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
245 {
246 switch (vrc)
247 {
248 case 'e':
249 /* already handled above */
250 break;
251
252 case 'u':
253 fUnregister = true;
254 fRun = false;
255 break;
256
257 case 'r':
258 fRegister = true;
259 fRun = false;
260 break;
261
262 case 'f':
263 fUnregister = true;
264 fRegister = true;
265 fRun = false;
266 break;
267
268 case 'H':
269 pszPipeName = ValueUnion.psz;
270 if (!pszPipeName)
271 pszPipeName = "";
272 fRun = false;
273 break;
274
275 case 'F':
276 pszLogFile = ValueUnion.psz;
277 break;
278
279 case 'R':
280 cHistory = ValueUnion.u32;
281 break;
282
283 case 'S':
284 uHistoryFileSize = ValueUnion.u64;
285 break;
286
287 case 'I':
288 uHistoryFileTime = ValueUnion.u32;
289 break;
290
291 case 'h':
292 {
293 TCHAR txt[]= L"Options:\n\n"
294 L"/RegServer:\tregister COM out-of-proc server\n"
295 L"/UnregServer:\tunregister COM out-of-proc server\n"
296 L"/ReregServer:\tunregister and register COM server\n"
297 L"no options:\trun the server";
298 TCHAR title[]=_T("Usage");
299 fRun = false;
300 MessageBox(NULL, txt, title, MB_OK);
301 return 0;
302 }
303
304 case 'V':
305 {
306 char *psz = NULL;
307 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
308 PRTUTF16 txt = NULL;
309 RTStrToUtf16(psz, &txt);
310 TCHAR title[]=_T("Version");
311 fRun = false;
312 MessageBox(NULL, txt, title, MB_OK);
313 RTStrFree(psz);
314 RTUtf16Free(txt);
315 return 0;
316 }
317
318 default:
319 /** @todo this assumes that stderr is visible, which is not
320 * true for standard Windows applications. */
321 /* continue on command line errors... */
322 RTGetOptPrintError(vrc, &ValueUnion);
323 }
324 }
325
326 /* Only create the log file when running VBoxSVC normally, but not when
327 * registering/unregistering or calling the helper functionality. */
328 if (fRun)
329 {
330 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
331 char szLogFile[RTPATH_MAX];
332 if (!pszLogFile)
333 {
334 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
335 if (RT_SUCCESS(vrc))
336 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
337 }
338 else
339 {
340 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
341 vrc = VERR_NO_MEMORY;
342 }
343 if (RT_FAILURE(vrc))
344 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
345
346 char szError[RTPATH_MAX + 128];
347 vrc = com::VBoxLogRelCreate("COM Server", szLogFile,
348 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
349 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
350 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
351 cHistory, uHistoryFileTime, uHistoryFileSize,
352 szError, sizeof(szError));
353 if (RT_FAILURE(vrc))
354 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, vrc);
355 }
356
357 int nRet = 0;
358 HRESULT hRes = com::Initialize();
359
360 _ASSERTE(SUCCEEDED(hRes));
361 _Module.Init(ObjectMap, hInstance, &LIBID_VirtualBox);
362 _Module.dwThreadID = GetCurrentThreadId();
363
364 if (!fRun)
365 {
366 if (fUnregister)
367 {
368 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
369 nRet = _Module.UnregisterServer(TRUE);
370 }
371 if (fRegister)
372 {
373 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
374 nRet = _Module.RegisterServer(TRUE);
375 }
376 if (pszPipeName)
377 {
378 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
379
380 if (!*pszPipeName)
381 vrc = VERR_INVALID_PARAMETER;
382
383 if (RT_SUCCESS(vrc))
384 {
385 /* do the helper job */
386 SVCHlpServer server;
387 vrc = server.open(pszPipeName);
388 if (RT_SUCCESS(vrc))
389 vrc = server.run();
390 }
391 if (RT_FAILURE(vrc))
392 {
393 Log(("SVCMAIN: Failed to process Helper request (%Rrc).", vrc));
394 nRet = 1;
395 }
396 }
397 }
398 else
399 {
400 _Module.StartMonitor();
401#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
402 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
403 _ASSERTE(SUCCEEDED(hRes));
404 hRes = CoResumeClassObjects();
405#else
406 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
407#endif
408 _ASSERTE(SUCCEEDED(hRes));
409
410 MSG msg;
411 while (GetMessage(&msg, 0, 0, 0))
412 DispatchMessage(&msg);
413
414 _Module.RevokeClassObjects();
415 Sleep(dwPause); //wait for any threads to finish
416 }
417
418 _Module.Term();
419
420 com::Shutdown();
421
422 Log(("SVCMAIN: Returning, COM server process ends.\n"));
423 return nRet;
424}
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