VirtualBox

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

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

Main/src-server/win/svcmain.cpp: on Windows (with working file locking and the known difficulties with renaming open files) suppress release log rotation on VBoxSVC termination, it can cause problems when a new process is launched at this point

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 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 /* Disable log rotation at this point, worst case a log file
96 * becomes slightly bigger than it should. Avoids quirks with
97 * log rotation: there might be another API service process
98 * running at this point which would rotate the logs concurrently,
99 * creating a mess. */
100 PRTLOGGER pReleaseLogger = RTLogRelDefaultInstance();
101 if (pReleaseLogger)
102 {
103 char szDest[1024];
104 int rc = RTLogGetDestinations(pReleaseLogger, szDest, sizeof(szDest));
105 if (RT_SUCCESS(rc))
106 {
107 rc = RTStrCat(szDest, sizeof(szDest), " nohistory");
108 if (RT_SUCCESS(rc))
109 {
110 rc = RTLogDestinations(pReleaseLogger, szDest);
111 AssertRC(rc);
112 }
113 }
114 }
115#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
116 CoSuspendClassObjects();
117 if (!bActivity && m_nLockCnt == 0)
118#endif
119 break;
120 }
121 }
122 CloseHandle(hEventShutdown);
123 PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
124}
125
126bool CExeModule::StartMonitor()
127{
128 hEventShutdown = CreateEvent(NULL, false, false, NULL);
129 if (hEventShutdown == NULL)
130 return false;
131 DWORD dwThreadID;
132 HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &dwThreadID);
133 return (h != NULL);
134}
135
136CExeModule _Module;
137
138BEGIN_OBJECT_MAP(ObjectMap)
139 OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox)
140END_OBJECT_MAP()
141
142
143LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2)
144{
145 while (p1 != NULL && *p1 != NULL)
146 {
147 LPCTSTR p = p2;
148 while (p != NULL && *p != NULL)
149 {
150 if (*p1 == *p)
151 return CharNext(p1);
152 p = CharNext(p);
153 }
154 p1 = CharNext(p1);
155 }
156 return NULL;
157}
158
159static int WordCmpI(LPCTSTR psz1, LPCTSTR psz2) throw()
160{
161 TCHAR c1 = (TCHAR)CharUpper((LPTSTR)*psz1);
162 TCHAR c2 = (TCHAR)CharUpper((LPTSTR)*psz2);
163 while (c1 != NULL && c1 == c2 && c1 != ' ' && c1 != '\t')
164 {
165 psz1 = CharNext(psz1);
166 psz2 = CharNext(psz2);
167 c1 = (TCHAR)CharUpper((LPTSTR)*psz1);
168 c2 = (TCHAR)CharUpper((LPTSTR)*psz2);
169 }
170 if ((c1 == NULL || c1 == ' ' || c1 == '\t') && (c2 == NULL || c2 == ' ' || c2 == '\t'))
171 return 0;
172
173 return (c1 < c2) ? -1 : 1;
174}
175
176/////////////////////////////////////////////////////////////////////////////
177//
178int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
179{
180 LPCTSTR lpCmdLine = GetCommandLine(); /* this line necessary for _ATL_MIN_CRT */
181
182 /*
183 * Need to parse the command line before initializing the VBox runtime.
184 */
185 TCHAR szTokens[] = _T("-/");
186 LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens);
187 while (lpszToken != NULL)
188 {
189 if (WordCmpI(lpszToken, _T("Embedding")) == 0)
190 {
191 /* %HOMEDRIVE%%HOMEPATH% */
192 wchar_t wszHome[RTPATH_MAX];
193 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
194 if (cEnv && cEnv < RTPATH_MAX)
195 {
196 DWORD cwc = cEnv; /* doesn't include NUL */
197 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
198 if (cEnv && cEnv < RTPATH_MAX - cwc)
199 {
200 /* If this fails there is nothing we can do. Ignore. */
201 SetCurrentDirectory(wszHome);
202 }
203 }
204 }
205
206 lpszToken = FindOneOf(lpszToken, szTokens);
207 }
208
209 /*
210 * Initialize the VBox runtime without loading
211 * the support driver.
212 */
213 int argc = __argc;
214 char **argv = __argv;
215 RTR3InitExe(argc, &argv, 0);
216
217 /* Note that all options are given lowercase/camel case/uppercase to
218 * approximate case insensitive matching, which RTGetOpt doesn't offer. */
219 static const RTGETOPTDEF s_aOptions[] =
220 {
221 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
222 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
223 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
224 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
225 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
226 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
227 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
228 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
229 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
230 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
231 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
232 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
233 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
234 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
235 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
236 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
237 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
238 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
239 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
240 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
241 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
242 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
243 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
244 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
245 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
246 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
247 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
248 };
249
250 bool fRun = true;
251 bool fRegister = false;
252 bool fUnregister = false;
253 const char *pszPipeName = NULL;
254 const char *pszLogFile = NULL;
255 uint32_t cHistory = 10; // enable log rotation, 10 files
256 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
257 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
258
259 RTGETOPTSTATE GetOptState;
260 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
261 AssertRC(vrc);
262
263 RTGETOPTUNION ValueUnion;
264 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
265 {
266 switch (vrc)
267 {
268 case 'e':
269 /* already handled above */
270 break;
271
272 case 'u':
273 fUnregister = true;
274 fRun = false;
275 break;
276
277 case 'r':
278 fRegister = true;
279 fRun = false;
280 break;
281
282 case 'f':
283 fUnregister = true;
284 fRegister = true;
285 fRun = false;
286 break;
287
288 case 'H':
289 pszPipeName = ValueUnion.psz;
290 if (!pszPipeName)
291 pszPipeName = "";
292 fRun = false;
293 break;
294
295 case 'F':
296 pszLogFile = ValueUnion.psz;
297 break;
298
299 case 'R':
300 cHistory = ValueUnion.u32;
301 break;
302
303 case 'S':
304 uHistoryFileSize = ValueUnion.u64;
305 break;
306
307 case 'I':
308 uHistoryFileTime = ValueUnion.u32;
309 break;
310
311 case 'h':
312 {
313 TCHAR txt[]= L"Options:\n\n"
314 L"/RegServer:\tregister COM out-of-proc server\n"
315 L"/UnregServer:\tunregister COM out-of-proc server\n"
316 L"/ReregServer:\tunregister and register COM server\n"
317 L"no options:\trun the server";
318 TCHAR title[]=_T("Usage");
319 fRun = false;
320 MessageBox(NULL, txt, title, MB_OK);
321 return 0;
322 }
323
324 case 'V':
325 {
326 char *psz = NULL;
327 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
328 PRTUTF16 txt = NULL;
329 RTStrToUtf16(psz, &txt);
330 TCHAR title[]=_T("Version");
331 fRun = false;
332 MessageBox(NULL, txt, title, MB_OK);
333 RTStrFree(psz);
334 RTUtf16Free(txt);
335 return 0;
336 }
337
338 default:
339 /** @todo this assumes that stderr is visible, which is not
340 * true for standard Windows applications. */
341 /* continue on command line errors... */
342 RTGetOptPrintError(vrc, &ValueUnion);
343 }
344 }
345
346 /* Only create the log file when running VBoxSVC normally, but not when
347 * registering/unregistering or calling the helper functionality. */
348 if (fRun)
349 {
350 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
351 char szLogFile[RTPATH_MAX];
352 if (!pszLogFile)
353 {
354 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
355 if (RT_SUCCESS(vrc))
356 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
357 }
358 else
359 {
360 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
361 vrc = VERR_NO_MEMORY;
362 }
363 if (RT_FAILURE(vrc))
364 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
365
366 char szError[RTPATH_MAX + 128];
367 vrc = com::VBoxLogRelCreate("COM Server", szLogFile,
368 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
369 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
370 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
371 cHistory, uHistoryFileTime, uHistoryFileSize,
372 szError, sizeof(szError));
373 if (RT_FAILURE(vrc))
374 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, vrc);
375 }
376
377 int nRet = 0;
378 HRESULT hRes = com::Initialize();
379
380 _ASSERTE(SUCCEEDED(hRes));
381 _Module.Init(ObjectMap, hInstance, &LIBID_VirtualBox);
382 _Module.dwThreadID = GetCurrentThreadId();
383
384 if (!fRun)
385 {
386 if (fUnregister)
387 {
388 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
389 nRet = _Module.UnregisterServer(TRUE);
390 }
391 if (fRegister)
392 {
393 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
394 nRet = _Module.RegisterServer(TRUE);
395 }
396 if (pszPipeName)
397 {
398 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
399
400 if (!*pszPipeName)
401 vrc = VERR_INVALID_PARAMETER;
402
403 if (RT_SUCCESS(vrc))
404 {
405 /* do the helper job */
406 SVCHlpServer server;
407 vrc = server.open(pszPipeName);
408 if (RT_SUCCESS(vrc))
409 vrc = server.run();
410 }
411 if (RT_FAILURE(vrc))
412 {
413 Log(("SVCMAIN: Failed to process Helper request (%Rrc).", vrc));
414 nRet = 1;
415 }
416 }
417 }
418 else
419 {
420 _Module.StartMonitor();
421#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
422 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
423 _ASSERTE(SUCCEEDED(hRes));
424 hRes = CoResumeClassObjects();
425#else
426 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
427#endif
428 _ASSERTE(SUCCEEDED(hRes));
429
430 MSG msg;
431 while (GetMessage(&msg, 0, 0, 0))
432 DispatchMessage(&msg);
433
434 _Module.RevokeClassObjects();
435 Sleep(dwPause); //wait for any threads to finish
436 }
437
438 _Module.Term();
439
440 com::Shutdown();
441
442 Log(("SVCMAIN: Returning, COM server process ends.\n"));
443 return nRet;
444}
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