VirtualBox

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

Last change on this file since 60865 was 60865, checked in by vboxsync, 9 years ago

Never use static instances of CComModule as it messes up the log filename by using VBoxRT.dll before it's initialized.

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