VirtualBox

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

Last change on this file since 37666 was 37666, checked in by vboxsync, 14 years ago

Main/VBoxSVC: add release logging, with conservative log rotation settings enabled by default

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

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