VirtualBox

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

Last change on this file since 53517 was 52489, checked in by vboxsync, 10 years ago

Main: tabs

  • 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 /* 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
143/////////////////////////////////////////////////////////////////////////////
144//
145int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
146{
147 int argc = __argc;
148 char **argv = __argv;
149
150 /*
151 * Need to parse the command line before initializing the VBox runtime so we can
152 * change to the user home directory before logs are being created.
153 */
154 for (int i = 1; i < argc; i++)
155 if ( (argv[i][0] == '/' || argv[i][0] == '-')
156 && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
157 {
158 /* %HOMEDRIVE%%HOMEPATH% */
159 wchar_t wszHome[RTPATH_MAX];
160 DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
161 if (cEnv && cEnv < RTPATH_MAX)
162 {
163 DWORD cwc = cEnv; /* doesn't include NUL */
164 cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
165 if (cEnv && cEnv < RTPATH_MAX - cwc)
166 {
167 /* If this fails there is nothing we can do. Ignore. */
168 SetCurrentDirectory(wszHome);
169 }
170 }
171 }
172
173 /*
174 * Initialize the VBox runtime without loading
175 * the support driver.
176 */
177 RTR3InitExe(argc, &argv, 0);
178
179 /* Note that all options are given lowercase/camel case/uppercase to
180 * approximate case insensitive matching, which RTGetOpt doesn't offer. */
181 static const RTGETOPTDEF s_aOptions[] =
182 {
183 { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
184 { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
185 { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
186 { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
187 { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
188 { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
189 { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
190 { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
191 { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
192 { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
193 { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
194 { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
195 { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
196 { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
197 { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
198 { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
199 { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
200 { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
201 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
202 { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
203 { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
204 { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
205 { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
206 { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
207 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
208 { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
209 { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
210 };
211
212 bool fRun = true;
213 bool fRegister = false;
214 bool fUnregister = false;
215 const char *pszPipeName = NULL;
216 const char *pszLogFile = NULL;
217 uint32_t cHistory = 10; // enable log rotation, 10 files
218 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
219 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
220
221 RTGETOPTSTATE GetOptState;
222 int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
223 AssertRC(vrc);
224
225 RTGETOPTUNION ValueUnion;
226 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
227 {
228 switch (vrc)
229 {
230 case 'e':
231 /* already handled above */
232 break;
233
234 case 'u':
235 fUnregister = true;
236 fRun = false;
237 break;
238
239 case 'r':
240 fRegister = true;
241 fRun = false;
242 break;
243
244 case 'f':
245 fUnregister = true;
246 fRegister = true;
247 fRun = false;
248 break;
249
250 case 'H':
251 pszPipeName = ValueUnion.psz;
252 if (!pszPipeName)
253 pszPipeName = "";
254 fRun = false;
255 break;
256
257 case 'F':
258 pszLogFile = ValueUnion.psz;
259 break;
260
261 case 'R':
262 cHistory = ValueUnion.u32;
263 break;
264
265 case 'S':
266 uHistoryFileSize = ValueUnion.u64;
267 break;
268
269 case 'I':
270 uHistoryFileTime = ValueUnion.u32;
271 break;
272
273 case 'h':
274 {
275 TCHAR txt[]= L"Options:\n\n"
276 L"/RegServer:\tregister COM out-of-proc server\n"
277 L"/UnregServer:\tunregister COM out-of-proc server\n"
278 L"/ReregServer:\tunregister and register COM server\n"
279 L"no options:\trun the server";
280 TCHAR title[]=_T("Usage");
281 fRun = false;
282 MessageBox(NULL, txt, title, MB_OK);
283 return 0;
284 }
285
286 case 'V':
287 {
288 char *psz = NULL;
289 RTStrAPrintf(&psz, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
290 PRTUTF16 txt = NULL;
291 RTStrToUtf16(psz, &txt);
292 TCHAR title[]=_T("Version");
293 fRun = false;
294 MessageBox(NULL, txt, title, MB_OK);
295 RTStrFree(psz);
296 RTUtf16Free(txt);
297 return 0;
298 }
299
300 default:
301 /** @todo this assumes that stderr is visible, which is not
302 * true for standard Windows applications. */
303 /* continue on command line errors... */
304 RTGetOptPrintError(vrc, &ValueUnion);
305 }
306 }
307
308 /* Only create the log file when running VBoxSVC normally, but not when
309 * registering/unregistering or calling the helper functionality. */
310 if (fRun)
311 {
312 /** @todo Merge this code with server.cpp (use Logging.cpp?). */
313 char szLogFile[RTPATH_MAX];
314 if (!pszLogFile)
315 {
316 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
317 if (RT_SUCCESS(vrc))
318 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
319 }
320 else
321 {
322 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
323 vrc = VERR_NO_MEMORY;
324 }
325 if (RT_FAILURE(vrc))
326 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
327
328 char szError[RTPATH_MAX + 128];
329 vrc = com::VBoxLogRelCreate("COM Server", szLogFile,
330 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
331 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
332 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
333 cHistory, uHistoryFileTime, uHistoryFileSize,
334 szError, sizeof(szError));
335 if (RT_FAILURE(vrc))
336 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, vrc);
337 }
338
339 int nRet = 0;
340 HRESULT hRes = com::Initialize();
341
342 _ASSERTE(SUCCEEDED(hRes));
343 _Module.Init(ObjectMap, hInstance, &LIBID_VirtualBox);
344 _Module.dwThreadID = GetCurrentThreadId();
345
346 if (!fRun)
347 {
348 if (fUnregister)
349 {
350 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
351 nRet = _Module.UnregisterServer(TRUE);
352 }
353 if (fRegister)
354 {
355 _Module.UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
356 nRet = _Module.RegisterServer(TRUE);
357 }
358 if (pszPipeName)
359 {
360 Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
361
362 if (!*pszPipeName)
363 vrc = VERR_INVALID_PARAMETER;
364
365 if (RT_SUCCESS(vrc))
366 {
367 /* do the helper job */
368 SVCHlpServer server;
369 vrc = server.open(pszPipeName);
370 if (RT_SUCCESS(vrc))
371 vrc = server.run();
372 }
373 if (RT_FAILURE(vrc))
374 {
375 Log(("SVCMAIN: Failed to process Helper request (%Rrc).", vrc));
376 nRet = 1;
377 }
378 }
379 }
380 else
381 {
382 _Module.StartMonitor();
383#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
384 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
385 _ASSERTE(SUCCEEDED(hRes));
386 hRes = CoResumeClassObjects();
387#else
388 hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
389#endif
390 _ASSERTE(SUCCEEDED(hRes));
391
392 MSG msg;
393 while (GetMessage(&msg, 0, 0, 0))
394 DispatchMessage(&msg);
395
396 _Module.RevokeClassObjects();
397 Sleep(dwPause); //wait for any threads to finish
398 }
399
400 _Module.Term();
401
402 com::Shutdown();
403
404 Log(("SVCMAIN: Returning, COM server process ends.\n"));
405 return nRet;
406}
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