VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/xpcom/server.cpp@ 103300

Last change on this file since 103300 was 103300, checked in by vboxsync, 4 months ago

libs/xpcom,Main: Disabled code to move VBoxXPCOMIPCD into VBoxSVC, bugref:10594

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.4 KB
RevLine 
[7964]1/* $Id: server.cpp 103300 2024-02-11 20:33:57Z vboxsync $ */
[1]2/** @file
[7964]3 * XPCOM server process (VBoxSVC) start point.
[1]4 */
5
6/*
[98103]7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
[1]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[1]26 */
27
[76592]28#define LOG_GROUP LOG_GROUP_MAIN_VBOXSVC
[1]29#include <ipcIService.h>
30#include <ipcCID.h>
31
32#include <nsIComponentRegistrar.h>
33
[5504]34#include <nsGenericFactory.h>
[1]35
[35368]36#include "server.h"
[2976]37
[76592]38#include "LoggingNew.h"
[1]39
[13908]40#include <VBox/param.h>
[82921]41#include <VBox/version.h>
[13908]42
[26517]43#include <iprt/buildconfig.h>
[14831]44#include <iprt/initterm.h>
[24828]45#include <iprt/critsect.h>
46#include <iprt/getopt.h>
[24830]47#include <iprt/message.h>
[37666]48#include <iprt/string.h>
[24830]49#include <iprt/stream.h>
[1]50#include <iprt/path.h>
[102048]51#include <iprt/pipe.h>
52#include <iprt/process.h>
[1471]53#include <iprt/timer.h>
[33112]54#include <iprt/env.h>
[1471]55
[24821]56#include <signal.h> // for the signal handler
[1]57#include <stdlib.h>
58#include <unistd.h>
59#include <errno.h>
[24823]60#include <fcntl.h>
61#include <sys/stat.h>
[24828]62#include <sys/resource.h>
[16267]63
[1]64/////////////////////////////////////////////////////////////////////////////
65// VirtualBox component instantiation
66/////////////////////////////////////////////////////////////////////////////
67
68#include <nsIGenericFactory.h>
[72975]69#include <VBox/com/VirtualBox.h>
[1]70
[58579]71#include "VBox/com/NativeEventQueue.h"
72
[35976]73#include "ApplianceImpl.h"
74#include "AudioAdapterImpl.h"
75#include "BandwidthControlImpl.h"
76#include "BandwidthGroupImpl.h"
[47018]77#include "NetworkServiceRunner.h"
[30760]78#include "DHCPServerImpl.h"
[35976]79#include "GuestOSTypeImpl.h"
80#include "HostImpl.h"
81#include "HostNetworkInterfaceImpl.h"
82#include "MachineImpl.h"
83#include "MediumFormatImpl.h"
84#include "MediumImpl.h"
85#include "NATEngineImpl.h"
86#include "NetworkAdapterImpl.h"
87#include "ParallelPortImpl.h"
88#include "ProgressProxyImpl.h"
89#include "SerialPortImpl.h"
90#include "SharedFolderImpl.h"
91#include "SnapshotImpl.h"
92#include "StorageControllerImpl.h"
93#include "SystemPropertiesImpl.h"
94#include "USBControllerImpl.h"
[47376]95#include "USBDeviceFiltersImpl.h"
[35976]96#include "VFSExplorerImpl.h"
97#include "VirtualBoxImpl.h"
98#include "VRDEServerImpl.h"
[7964]99#ifdef VBOX_WITH_USB
[35976]100# include "HostUSBDeviceImpl.h"
[30856]101# include "USBDeviceFilterImpl.h"
[35976]102# include "USBDeviceImpl.h"
[7964]103#endif
[33474]104#ifdef VBOX_WITH_EXTPACK
[35976]105# include "ExtPackManagerImpl.h"
[33474]106#endif
[45138]107# include "NATNetworkImpl.h"
[1]108
[50369]109// This needs to stay - it is needed by the service registration below, and
110// is defined in the automatically generated VirtualBoxWrap.cpp
111extern nsIClassInfo *NS_CLASSINFO_NAME(VirtualBoxWrap);
112NS_DECL_CI_INTERFACE_GETTER(VirtualBoxWrap)
[45138]113
[1]114////////////////////////////////////////////////////////////////////////////////
115
[1471]116static bool gAutoShutdown = false;
[39459]117/** Delay before shutting down the VirtualBox server after the last
118 * VirtualBox instance is released, in ms */
119static uint32_t gShutdownDelayMs = 5000;
[1471]120
[58579]121static com::NativeEventQueue *gEventQ = NULL;
[31872]122static PRBool volatile gKeepRunning = PR_TRUE;
123static PRBool volatile gAllowSigUsrQuit = PR_TRUE;
[1]124
[2331]125/////////////////////////////////////////////////////////////////////////////
126
127/**
[1]128 * VirtualBox class factory that destroys the created instance right after
129 * the last reference to it is released by the client, and recreates it again
130 * when necessary (so VirtualBox acts like a singleton object).
131 */
132class VirtualBoxClassFactory : public VirtualBox
133{
134public:
135
136 virtual ~VirtualBoxClassFactory()
137 {
[24828]138 LogFlowFunc(("Deleting VirtualBox...\n"));
[1]139
140 FinalRelease();
[15604]141 sInstance = NULL;
[1]142
[24828]143 LogFlowFunc(("VirtualBox object deleted.\n"));
[24830]144 RTPrintf("Informational: VirtualBox object deleted.\n");
[1]145 }
146
147 NS_IMETHOD_(nsrefcnt) Release()
148 {
[1471]149 /* we overload Release() to guarantee the VirtualBox destructor is
150 * always called on the main thread */
[1]151
152 nsrefcnt count = VirtualBox::Release();
153
154 if (count == 1)
155 {
[1471]156 /* the last reference held by clients is being released
157 * (see GetInstance()) */
[1]158
[58579]159 bool onMainThread = RTThreadIsMain(RTThreadSelf());
[2057]160 PRBool timerStarted = PR_FALSE;
[1835]161
[15604]162 /* sTimer is null if this call originates from FactoryDestructor()*/
[2057]163 if (sTimer != NULL)
[1]164 {
[24828]165 LogFlowFunc(("Last VirtualBox instance was released.\n"));
[39459]166 LogFlowFunc(("Scheduling server shutdown in %u ms...\n",
167 gShutdownDelayMs));
[2057]168
[2331]169 /* make sure the previous timer (if any) is stopped;
170 * otherwise RTTimerStart() will definitely fail. */
[24828]171 RTTimerLRStop(sTimer);
[2331]172
[39459]173 int vrc = RTTimerLRStart(sTimer, gShutdownDelayMs * RT_NS_1MS_64);
[24828]174 AssertRC(vrc);
[85270]175 timerStarted = RT_BOOL(RT_SUCCESS(vrc));
[2057]176 }
177 else
178 {
[24828]179 LogFlowFunc(("Last VirtualBox instance was released "
180 "on XPCOM shutdown.\n"));
181 Assert(onMainThread);
[2057]182 }
183
[31872]184 gAllowSigUsrQuit = PR_TRUE;
185
[2057]186 if (!timerStarted)
187 {
[1835]188 if (!onMainThread)
[1]189 {
[2057]190 /* Failed to start the timer, post the shutdown event
[33540]191 * manually if not on the main thread already. */
[24828]192 ShutdownTimer(NULL, NULL, 0);
[1]193 }
[1835]194 else
195 {
196 /* Here we come if:
197 *
198 * a) gEventQ is 0 which means either FactoryDestructor() is called
199 * or the IPC/DCONNECT shutdown sequence is initiated by the
200 * XPCOM shutdown routine (NS_ShutdownXPCOM()), which always
201 * happens on the main thread.
202 *
203 * b) gEventQ has reported we're on the main thread. This means
204 * that DestructEventHandler() has been called, but another
205 * client was faster and requested VirtualBox again.
206 *
[2057]207 * In either case, there is nothing to do.
[2980]208 *
[2057]209 * Note: case b) is actually no more valid since we don't
210 * call Release() from DestructEventHandler() in this case
211 * any more. Thus, we assert below.
[1835]212 */
[2057]213
[55458]214 Assert(!gEventQ);
[1835]215 }
[1]216 }
217 }
218
219 return count;
220 }
221
[58579]222 class MaybeQuitEvent : public NativeEvent
[1835]223 {
[61714]224 public:
225 MaybeQuitEvent() :
226 m_fSignal(false)
227 {
228 }
229
230 MaybeQuitEvent(bool fSignal) :
231 m_fSignal(fSignal)
232 {
233 }
234
235 private:
[2331]236 /* called on the main thread */
237 void *handler()
238 {
[39459]239 LogFlowFuncEnter();
[1835]240
[24828]241 Assert(RTCritSectIsInitialized(&sLock));
[1]242
[2331]243 /* stop accepting GetInstance() requests on other threads during
244 * possible destruction */
[24828]245 RTCritSectEnter(&sLock);
[1]246
[65361]247 nsrefcnt count = 1;
[1]248
[2331]249 /* sInstance is NULL here if it was deleted immediately after
250 * creation due to initialization error. See GetInstance(). */
251 if (sInstance != NULL)
252 {
[65361]253 /* Safe way to get current refcount is by first increasing and
254 * then decreasing. Keep in mind that the Release is overloaded
255 * (see VirtualBoxClassFactory::Release) and will start the
256 * timer again if the returned count is 1. It won't do harm,
257 * but also serves no purpose, so stop it ASAP. */
258 sInstance->AddRef();
[2331]259 count = sInstance->Release();
[65361]260 if (count == 1)
261 {
262 RTTimerLRStop(sTimer);
263 /* Release the guard reference added in GetInstance() */
264 sInstance->Release();
265 }
[2331]266 }
[1835]267
[65361]268 if (count == 1)
[2331]269 {
[61714]270 if (gAutoShutdown || m_fSignal)
[2331]271 {
[24828]272 Assert(sInstance == NULL);
273 LogFlowFunc(("Terminating the server process...\n"));
[2331]274 /* make it leave the event loop */
275 gKeepRunning = PR_FALSE;
276 }
[39459]277 else
278 LogFlowFunc(("No automatic shutdown.\n"));
[2331]279 }
280 else
281 {
[15604]282 /* This condition is quite rare: a new client happened to
[2331]283 * connect after this event has been posted to the main queue
284 * but before it started to process it. */
[65246]285 LogRel(("Destruction is canceled (refcnt=%d).\n", count));
[2331]286 }
[1]287
[24828]288 RTCritSectLeave(&sLock);
[1]289
[39459]290 LogFlowFuncLeave();
[2331]291 return NULL;
292 }
[61714]293
294 bool m_fSignal;
[2331]295 };
[1]296
[57428]297 static DECLCALLBACK(void) ShutdownTimer(RTTIMERLR hTimerLR, void *pvUser, uint64_t /*iTick*/)
[1471]298 {
[24828]299 NOREF(hTimerLR);
300 NOREF(pvUser);
[1471]301
[1835]302 /* A "too late" event is theoretically possible if somebody
303 * manually ended the server after a destruction has been scheduled
304 * and this method was so lucky that it got a chance to run before
305 * the timer was killed. */
[58579]306 com::NativeEventQueue *q = gEventQ;
[55458]307 AssertReturnVoid(q);
[1835]308
[2331]309 /* post a quit event to the main queue */
[61714]310 MaybeQuitEvent *ev = new MaybeQuitEvent(false /* fSignal */);
[58579]311 if (!q->postEvent(ev))
312 delete ev;
[1471]313
[2331]314 /* A failure above means we've been already stopped (for example
315 * by Ctrl-C). FactoryDestructor() (NS_ShutdownXPCOM())
316 * will do the job. Nothing to do. */
[1471]317 }
318
[1]319 static NS_IMETHODIMP FactoryConstructor()
320 {
[24828]321 LogFlowFunc(("\n"));
[1]322
[1471]323 /* create a critsect to protect object construction */
[24828]324 if (RT_FAILURE(RTCritSectInit(&sLock)))
[1]325 return NS_ERROR_OUT_OF_MEMORY;
326
[24828]327 int vrc = RTTimerLRCreateEx(&sTimer, 0, 0, ShutdownTimer, NULL);
[21878]328 if (RT_FAILURE(vrc))
[1808]329 {
[24828]330 LogFlowFunc(("Failed to create a timer! (vrc=%Rrc)\n", vrc));
[1808]331 return NS_ERROR_FAILURE;
332 }
[1471]333
[1]334 return NS_OK;
335 }
336
337 static NS_IMETHODIMP FactoryDestructor()
338 {
[24828]339 LogFlowFunc(("\n"));
[1]340
[24828]341 RTTimerLRDestroy(sTimer);
[1471]342 sTimer = NULL;
343
[15604]344 if (sInstance != NULL)
[1]345 {
[33842]346 /* Either posting a destruction event failed for some reason (most
[1471]347 * likely, the quit event has been received before the last release),
348 * or the client has terminated abnormally w/o releasing its
349 * VirtualBox instance (so NS_ShutdownXPCOM() is doing a cleanup).
[2331]350 * Release the guard reference we added in GetInstance(). */
[1]351 sInstance->Release();
352 }
353
[55458]354 /* Destroy lock after releasing the VirtualBox instance, otherwise
355 * there are races with cleanup. */
356 RTCritSectDelete(&sLock);
357
[1]358 return NS_OK;
359 }
360
[24828]361 static nsresult GetInstance(VirtualBox **inst)
[1]362 {
[24828]363 LogFlowFunc(("Getting VirtualBox object...\n"));
[1]364
[24828]365 RTCritSectEnter(&sLock);
[1]366
[15604]367 if (!gKeepRunning)
368 {
[24828]369 LogFlowFunc(("Process termination requested first. Refusing.\n"));
[1]370
[24828]371 RTCritSectLeave(&sLock);
[15604]372
373 /* this rv is what CreateInstance() on the client side returns
374 * when the server process stops accepting events. Do the same
375 * here. The client wrapper should attempt to start a new process in
376 * response to a failure from us. */
377 return NS_ERROR_ABORT;
378 }
379
380 nsresult rv = NS_OK;
381
382 if (sInstance == NULL)
[1]383 {
[49953]384 LogFlowFunc(("Creating new VirtualBox object...\n"));
[1]385 sInstance = new VirtualBoxClassFactory();
[15604]386 if (sInstance != NULL)
[1]387 {
[1471]388 /* make an extra AddRef to take the full control
389 * on the VirtualBox destruction (see FinalRelease()) */
[1]390 sInstance->AddRef();
391
[1471]392 sInstance->AddRef(); /* protect FinalConstruct() */
[1]393 rv = sInstance->FinalConstruct();
[24830]394 RTPrintf("Informational: VirtualBox object created (rc=%Rhrc).\n", rv);
[24828]395 if (NS_FAILED(rv))
[1]396 {
[2331]397 /* On failure diring VirtualBox initialization, delete it
398 * immediately on the current thread by releasing all
399 * references in order to properly schedule the server
400 * shutdown. Since the object is fully deleted here, there
401 * is a chance to fix the error and request a new
402 * instantiation before the server terminates. However,
[33540]403 * the main reason to maintain the shutdown delay on
[2331]404 * failure is to let the front-end completely fetch error
405 * info from a server-side IVirtualBoxErrorInfo object. */
406 sInstance->Release();
407 sInstance->Release();
[24828]408 Assert(sInstance == NULL);
[1]409 }
[2331]410 else
411 {
412 /* On success, make sure the previous timer is stopped to
413 * cancel a scheduled server termination (if any). */
[31872]414 gAllowSigUsrQuit = PR_FALSE;
[24828]415 RTTimerLRStop(sTimer);
[2331]416 }
[1]417 }
418 else
419 {
420 rv = NS_ERROR_OUT_OF_MEMORY;
421 }
422 }
423 else
424 {
[24828]425 LogFlowFunc(("Using existing VirtualBox object...\n"));
[1]426 nsrefcnt count = sInstance->AddRef();
[24828]427 Assert(count > 1);
[1]428
[65361]429 if (count >= 2)
[1]430 {
[33595]431 LogFlowFunc(("Another client has requested a reference to VirtualBox, canceling destruction...\n"));
[2980]432
[1835]433 /* make sure the previous timer is stopped */
[31872]434 gAllowSigUsrQuit = PR_FALSE;
[24828]435 RTTimerLRStop(sTimer);
[1]436 }
437 }
438
439 *inst = sInstance;
440
[24828]441 RTCritSectLeave(&sLock);
[1]442
443 return rv;
444 }
445
446private:
447
[1835]448 /* Don't be confused that sInstance is of the *ClassFactory type. This is
449 * actually a singleton instance (*ClassFactory inherits the singleton
450 * class; we combined them just for "simplicity" and used "static" for
451 * factory methods. *ClassFactory here is necessary for a couple of extra
452 * methods. */
453
454 static VirtualBoxClassFactory *sInstance;
[1]455 static RTCRITSECT sLock;
[1471]456
[10958]457 static RTTIMERLR sTimer;
[1]458};
459
[15604]460VirtualBoxClassFactory *VirtualBoxClassFactory::sInstance = NULL;
[24828]461RTCRITSECT VirtualBoxClassFactory::sLock;
[1]462
[10958]463RTTIMERLR VirtualBoxClassFactory::sTimer = NIL_RTTIMERLR;
[1]464
[24828]465NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR_WITH_RC(VirtualBox, VirtualBoxClassFactory::GetInstance)
[1471]466
[1]467////////////////////////////////////////////////////////////////////////////////
468
[36466]469typedef NSFactoryDestructorProcPtr NSFactoryConstructorProcPtr;
[1]470
471/**
472 * Enhanced module component information structure.
[5504]473 *
474 * nsModuleComponentInfo lacks the factory construction callback, here we add
[36466]475 * it. This callback is called straight after a nsGenericFactory instance is
476 * successfully created in RegisterSelfComponents.
[1]477 */
[36466]478struct nsModuleComponentInfoPlusFactoryConstructor
[1]479{
[36466]480 /** standard module component information */
481 const nsModuleComponentInfo *mpModuleComponentInfo;
[1]482 /** (optional) Factory Construction Callback */
[36466]483 NSFactoryConstructorProcPtr mFactoryConstructor;
[1]484};
485
486/////////////////////////////////////////////////////////////////////////////
487
488/**
[20630]489 * Helper function to register self components upon start-up
[1471]490 * of the out-of-proc server.
491 */
492static nsresult
[24828]493RegisterSelfComponents(nsIComponentRegistrar *registrar,
[36466]494 const nsModuleComponentInfoPlusFactoryConstructor *aComponents,
[24828]495 PRUint32 count)
[1]496{
497 nsresult rc = NS_OK;
[36466]498 const nsModuleComponentInfoPlusFactoryConstructor *info = aComponents;
[21878]499 for (PRUint32 i = 0; i < count && NS_SUCCEEDED(rc); i++, info++)
[1471]500 {
501 /* skip components w/o a constructor */
[36466]502 if (!info->mpModuleComponentInfo->mConstructor)
[24828]503 continue;
[1471]504 /* create a new generic factory for a component and register it */
[1]505 nsIGenericFactory *factory;
[36466]506 rc = NS_NewGenericFactory(&factory, info->mpModuleComponentInfo);
507 if (NS_SUCCEEDED(rc) && info->mFactoryConstructor)
508 {
509 rc = info->mFactoryConstructor();
510 if (NS_FAILED(rc))
511 NS_RELEASE(factory);
512 }
[21878]513 if (NS_SUCCEEDED(rc))
[1471]514 {
[36466]515 rc = registrar->RegisterFactory(info->mpModuleComponentInfo->mCID,
516 info->mpModuleComponentInfo->mDescription,
517 info->mpModuleComponentInfo->mContractID,
[24828]518 factory);
[36466]519 NS_RELEASE(factory);
[1]520 }
521 }
522 return rc;
523}
524
525/////////////////////////////////////////////////////////////////////////////
526
527static ipcIService *gIpcServ = nsnull;
[24828]528static const char *g_pszPidFile = NULL;
[1]529
[58579]530class ForceQuitEvent : public NativeEvent
[2331]531{
532 void *handler()
533 {
[24828]534 LogFlowFunc(("\n"));
[1]535
[2331]536 gKeepRunning = PR_FALSE;
537
[24828]538 if (g_pszPidFile)
539 RTFileDelete(g_pszPidFile);
[2331]540
541 return NULL;
542 }
543};
544
[31872]545static void signal_handler(int sig)
[1]546{
[58579]547 com::NativeEventQueue *q = gEventQ;
[55458]548 if (q && gKeepRunning)
[1]549 {
[31872]550 if (sig == SIGUSR1)
551 {
552 if (gAllowSigUsrQuit)
553 {
[61714]554 /* terminate the server process if it is idle */
555 VirtualBoxClassFactory::MaybeQuitEvent *ev = new VirtualBoxClassFactory::MaybeQuitEvent(true /* fSignal */);
[58579]556 if (!q->postEvent(ev))
557 delete ev;
[31872]558 }
559 /* else do nothing */
560 }
561 else
562 {
563 /* post a force quit event to the queue */
564 ForceQuitEvent *ev = new ForceQuitEvent();
[58579]565 if (!q->postEvent(ev))
566 delete ev;
[31872]567 }
[1]568 }
[2331]569}
[1]570
[37356]571static nsresult vboxsvcSpawnDaemonByReExec(const char *pszPath, bool fAutoShutdown, const char *pszPidFile)
[33112]572{
[102048]573 /*
574 * Setup an anonymous pipe that we can use to determine when the daemon
575 * process has started up. the daemon will write a char to the pipe, and
576 * when we read it, we'll know to proceed with trying to connect to the
577 * daemon.
578 */
579 RTPIPE hPipeWr = NIL_RTPIPE;
580 RTPIPE hPipeRd = NIL_RTPIPE;
581 int vrc = RTPipeCreate(&hPipeRd, &hPipeWr, RTPIPE_C_INHERIT_WRITE);
582 if (RT_SUCCESS(vrc))
[37356]583 {
[102048]584 char szPipeInheritFd[32]; RT_ZERO(szPipeInheritFd);
[33112]585
[102048]586 unsigned cArgs = 0;
587 const char *apszArgs[1 + 1 + 2 + 2 + 1];
588 apszArgs[cArgs++] = pszPath;
589 if (fAutoShutdown)
590 apszArgs[cArgs++] = "--auto-shutdown";
591 if (pszPidFile)
592 {
593 apszArgs[cArgs++] = "--pidfile";
594 apszArgs[cArgs++] = pszPidFile;
595 }
596 apszArgs[cArgs++] = "--inherit-startup-pipe";
597 apszArgs[cArgs++] = &szPipeInheritFd[0];
598 apszArgs[cArgs++] = NULL;
[33112]599
[102048]600 ssize_t cch = RTStrFormatU32(&szPipeInheritFd[0], sizeof(szPipeInheritFd),
601 (uint32_t)RTPipeToNative(hPipeWr), 10 /*uiBase*/,
602 0 /*cchWidth*/, 0 /*cchPrecision*/, 0 /*fFlags*/);
[102052]603 Assert(cch > 0); RT_NOREF(cch);
[33112]604
[102048]605 RTHANDLE hStdNil;
606 hStdNil.enmType = RTHANDLETYPE_FILE;
607 hStdNil.u.hFile = NIL_RTFILE;
[33112]608
[102048]609 vrc = RTProcCreateEx(pszPath, apszArgs, RTENV_DEFAULT,
610 RTPROC_FLAGS_DETACHED, &hStdNil, &hStdNil, &hStdNil,
611 NULL /* pszAsUser */, NULL /* pszPassword */, NULL /* pExtraData */,
612 NULL /* phProcess */);
613 if (RT_SUCCESS(vrc))
614 {
615 vrc = RTPipeClose(hPipeWr); AssertRC(vrc); RT_NOREF(vrc);
616 hPipeWr = NIL_RTPIPE;
[33112]617
[102048]618 size_t cbRead = 0;
619 char msg[10];
620 memset(msg, '\0', sizeof(msg));
621 vrc = RTPipeReadBlocking(hPipeRd, &msg[0], sizeof(msg) - 1, &cbRead);
622 if ( RT_SUCCESS(vrc)
623 && cbRead == 5
624 && !strcmp(msg, "READY"))
625 {
626 RTPipeClose(hPipeRd);
627 return NS_OK;
628 }
629 }
[33112]630
[102048]631 if (hPipeWr != NIL_RTPIPE)
632 RTPipeClose(hPipeWr);
633 RTPipeClose(hPipeRd);
634 }
[33112]635
[102048]636 return NS_ERROR_FAILURE;
[33112]637}
638
[82921]639static void showUsage(const char *pcszFileName)
640{
641 RTPrintf(VBOX_PRODUCT " VBoxSVC "
642 VBOX_VERSION_STRING "\n"
[96399]643 "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
[82921]644 RTPrintf("By default the service will be started in the background.\n"
645 "\n");
646 RTPrintf("Usage:\n"
647 "\n");
648 RTPrintf(" %s\n", pcszFileName);
649 RTPrintf("\n");
650 RTPrintf("Options:\n");
[102048]651 RTPrintf(" -a, --automate Start XPCOM on demand and daemonize.\n");
652 RTPrintf(" -A, --auto-shutdown Shuts down service if no longer in use.\n");
653 RTPrintf(" -d, --daemonize Starts service in background.\n");
654 RTPrintf(" -D, --shutdown-delay <ms> Sets shutdown delay in ms.\n");
655 RTPrintf(" -h, --help Displays this help.\n");
656 RTPrintf(" -p, --pidfile <path> Uses a specific pidfile.\n");
657 RTPrintf(" -F, --logfile <path> Uses a specific logfile.\n");
658 RTPrintf(" -R, --logrotate <count> Number of old log files to keep.\n");
659 RTPrintf(" -S, --logsize <bytes> Maximum size of a log file before rotating.\n");
660 RTPrintf(" -I, --loginterval <s> Maximum amount of time to put in a log file.\n");
661 RTPrintf(" -P, --inherit-startup-pipe <fd> The startup pipe file descriptor number when re-starting the daemon\n");
[82921]662
663 RTPrintf("\n");
664}
665
[103300]666#ifdef VBOX_WITH_XPCOMIPCD_IN_VBOX_SVC
667
668#include <iprt/thread.h>
669
670DECL_IMPORT_NOTHROW(int) RTCALL VBoxXpcomIpcdCreate(PRTTHREAD phThrdIpcd);
671DECL_IMPORT_NOTHROW(int) RTCALL VBoxXpcomIpcdDestroy(RTTHREAD hThrdIpcd);
672#endif
673
[24828]674int main(int argc, char **argv)
[1]675{
[24828]676 /*
677 * Initialize the VBox runtime without loading
678 * the support driver
679 */
[38636]680 int vrc = RTR3InitExe(argc, &argv, 0);
681 if (RT_FAILURE(vrc))
682 return RTMsgInitFailure(vrc);
[24828]683
684 static const RTGETOPTDEF s_aOptions[] =
[1]685 {
[102048]686 { "--automate", 'a', RTGETOPT_REQ_NOTHING },
687 { "--auto-shutdown", 'A', RTGETOPT_REQ_NOTHING },
688 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
689 { "--help", 'h', RTGETOPT_REQ_NOTHING },
690 { "--shutdown-delay", 'D', RTGETOPT_REQ_UINT32 },
691 { "--pidfile", 'p', RTGETOPT_REQ_STRING },
692 { "--logfile", 'F', RTGETOPT_REQ_STRING },
693 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
694 { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
695 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 },
696 { "--inherit-startup-pipe", 'P', RTGETOPT_REQ_UINT32 }
[1]697 };
698
[37666]699 const char *pszLogFile = NULL;
700 uint32_t cHistory = 10; // enable log rotation, 10 files
701 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
702 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
[102048]703 uint32_t uStartupPipeFd = UINT32_MAX;
[24828]704 bool fDaemonize = false;
705
706 RTGETOPTSTATE GetOptState;
[38636]707 vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
[24828]708 AssertRC(vrc);
709
710 RTGETOPTUNION ValueUnion;
711 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
[1]712 {
[24828]713 switch (vrc)
[1]714 {
[1471]715 case 'a':
716 /* --automate mode means we are started by XPCOM on
717 * demand. Daemonize ourselves and activate
718 * auto-shutdown. */
719 gAutoShutdown = true;
720 fDaemonize = true;
721 break;
722
[6826]723 case 'A':
[39459]724 /* --auto-shutdown mode means we're already daemonized. */
[6826]725 gAutoShutdown = true;
726 break;
727
[1]728 case 'd':
729 fDaemonize = true;
730 break;
731
[39459]732 case 'D':
733 gShutdownDelayMs = ValueUnion.u32;
734 break;
735
[1]736 case 'p':
[24828]737 g_pszPidFile = ValueUnion.psz;
[1]738 break;
739
[37666]740 case 'F':
741 pszLogFile = ValueUnion.psz;
742 break;
743
744 case 'R':
745 cHistory = ValueUnion.u32;
746 break;
747
748 case 'S':
749 uHistoryFileSize = ValueUnion.u64;
750 break;
751
752 case 'I':
753 uHistoryFileTime = ValueUnion.u32;
754 break;
755
[102048]756 case 'P':
757 uStartupPipeFd = ValueUnion.u32;
758 break;
759
[26517]760 case 'h':
[82921]761 showUsage(argv[0]);
[61196]762 return RTEXITCODE_SYNTAX;
[26517]763
764 case 'V':
765 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
[61196]766 return RTEXITCODE_SUCCESS;
[26517]767
[1]768 default:
[24828]769 return RTGetOptPrintError(vrc, &ValueUnion);
[1]770 }
771 }
772
773 if (fDaemonize)
774 {
[37356]775 vboxsvcSpawnDaemonByReExec(argv[0], gAutoShutdown, g_pszPidFile);
[24828]776 exit(126);
[1]777 }
778
[37666]779 nsresult rc;
[1]780
[46909]781 /** @todo Merge this code with svcmain.cpp (use Logging.cpp?). */
[46908]782 char szLogFile[RTPATH_MAX];
[37666]783 if (!pszLogFile)
784 {
785 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
786 if (RT_SUCCESS(vrc))
787 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
788 }
[46908]789 else
790 {
791 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
792 vrc = VERR_NO_MEMORY;
793 }
794 if (RT_FAILURE(vrc))
795 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
796
[69749]797 RTERRINFOSTATIC ErrInfo;
[46908]798 vrc = com::VBoxLogRelCreate("XPCOM Server", szLogFile,
[41040]799 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
[47525]800 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
[41040]801 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
802 cHistory, uHistoryFileTime, uHistoryFileSize,
[69749]803 RTErrInfoInitStatic(&ErrInfo));
[41040]804 if (RT_FAILURE(vrc))
[69749]805 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
[37666]806
[62222]807 /* Set up a build identifier so that it can be seen from core dumps what
808 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
809 static char saBuildID[48];
810 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
811 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
812
[36466]813 const nsModuleComponentInfo VirtualBoxInfo = {
814 "VirtualBox component",
[36478]815 NS_VIRTUALBOX_CID,
[36466]816 NS_VIRTUALBOX_CONTRACTID,
817 VirtualBoxConstructor, // constructor function
818 NULL, // registration function
819 NULL, // deregistration function
820 VirtualBoxClassFactory::FactoryDestructor, // factory destructor function
[50369]821 NS_CI_INTERFACE_GETTER_NAME(VirtualBoxWrap),
[36466]822 NULL, // language helper
[50369]823 &NS_CLASSINFO_NAME(VirtualBoxWrap),
[36466]824 0 // flags
825 };
826
827 const nsModuleComponentInfoPlusFactoryConstructor components[] = {
828 {
829 &VirtualBoxInfo,
830 VirtualBoxClassFactory::FactoryConstructor // factory constructor function
831 }
832 };
833
[103300]834#ifdef VBOX_WITH_XPCOMIPCD_IN_VBOX_SVC
835 RTTHREAD hThrdIpcd = NIL_RTTHREAD;
836 vrc = VBoxXpcomIpcdCreate(&hThrdIpcd);
837 if (RT_FAILURE(vrc))
838 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create IPC daemon thread -> %Rrc", vrc);
839#endif
840
[61196]841 do /* goto avoidance only */
[1]842 {
[2976]843 rc = com::Initialize();
[24828]844 if (NS_FAILED(rc))
[1]845 {
[24830]846 RTMsgError("Failed to initialize XPCOM! (rc=%Rhrc)\n", rc);
[1]847 break;
848 }
849
[50369]850 nsCOMPtr<nsIComponentRegistrar> registrar;
[24828]851 rc = NS_GetComponentRegistrar(getter_AddRefs(registrar));
852 if (NS_FAILED(rc))
[1471]853 {
[24830]854 RTMsgError("Failed to get component registrar! (rc=%Rhrc)", rc);
[1471]855 break;
856 }
857
[24828]858 registrar->AutoRegister(nsnull);
859 rc = RegisterSelfComponents(registrar, components,
[36466]860 NS_ARRAY_LENGTH(components));
[24828]861 if (NS_FAILED(rc))
[1]862 {
[24830]863 RTMsgError("Failed to register server components! (rc=%Rhrc)", rc);
[1]864 break;
865 }
866
[49953]867 nsCOMPtr<ipcIService> ipcServ(do_GetService(IPC_SERVICE_CONTRACTID, &rc));
[26186]868 if (NS_FAILED(rc))
[1]869 {
[24830]870 RTMsgError("Failed to get IPC service! (rc=%Rhrc)", rc);
[1]871 break;
872 }
873
[24828]874 NS_ADDREF(gIpcServ = ipcServ);
[1]875
[24828]876 LogFlowFunc(("Will use \"%s\" as server name.\n", VBOXSVC_IPC_NAME));
[1806]877
[24828]878 rc = gIpcServ->AddName(VBOXSVC_IPC_NAME);
879 if (NS_FAILED(rc))
[1]880 {
[24828]881 LogFlowFunc(("Failed to register the server name (rc=%Rhrc (%08X))!\n"
882 "Is another server already running?\n", rc, rc));
[8012]883
[24830]884 RTMsgError("Failed to register the server name \"%s\" (rc=%Rhrc)!\n"
885 "Is another server already running?\n",
886 VBOXSVC_IPC_NAME, rc);
[24828]887 NS_RELEASE(gIpcServ);
[1]888 break;
889 }
890
891 {
[1471]892 /* setup signal handling to convert some signals to a quit event */
[1]893 struct sigaction sa;
894 sa.sa_handler = signal_handler;
[24828]895 sigemptyset(&sa.sa_mask);
[1]896 sa.sa_flags = 0;
[24828]897 sigaction(SIGINT, &sa, NULL);
898 sigaction(SIGQUIT, &sa, NULL);
899 sigaction(SIGTERM, &sa, NULL);
[65420]900// XXX Temporary allow release assertions to terminate VBoxSVC
901// sigaction(SIGTRAP, &sa, NULL);
[31872]902 sigaction(SIGUSR1, &sa, NULL);
[1]903 }
904
905 {
[1397]906 char szBuf[80];
[62363]907 size_t cSize;
[1]908
[62363]909 cSize = RTStrPrintf(szBuf, sizeof(szBuf),
[26034]910 VBOX_PRODUCT" XPCOM Server Version "
[24828]911 VBOX_VERSION_STRING);
[62363]912 for (size_t i = cSize; i > 0; i--)
[1]913 putchar('*');
[24830]914 RTPrintf("\n%s\n", szBuf);
[96399]915 RTPrintf("Copyright (C) 2004-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
[1]916#ifdef DEBUG
[24830]917 RTPrintf("Debug version.\n");
[1]918#endif
919 }
920
[102048]921 if (uStartupPipeFd == UINT32_MAX)
[1]922 {
[102048]923 /* Check the environment variable. */
924 const char *pszStartupPipe = RTEnvGet("VBOX_STARTUP_PIPE_FD");
925 if (pszStartupPipe)
926 {
927 /* Convert it to a number. */
928 vrc = RTStrToUInt32Full(pszStartupPipe, 0, &uStartupPipeFd);
929 if (RT_FAILURE(vrc))
930 {
931 RTMsgError("Failed to parse VBOX_STARTUP_PIPE_FD=%s! (rc=%Rrc)", pszStartupPipe, vrc);
932 break;
933 }
934 }
935 }
936 if (uStartupPipeFd != UINT32_MAX)
937 {
[24830]938 RTPrintf("\nStarting event loop....\n[send TERM signal to quit]\n");
[102048]939
940 RTPIPE hPipe = NIL_RTPIPE;
941 vrc = RTPipeFromNative(&hPipe, (RTHCINTPTR)uStartupPipeFd, RTPIPE_N_WRITE);
942 if (RT_SUCCESS(vrc))
943 {
944 vrc = RTPipeWriteBlocking(hPipe, RT_STR_TUPLE("READY"), NULL /*pcbWritten*/);
945 AssertRC(vrc); RT_NOREF(vrc);
946
947 vrc = RTPipeClose(hPipe);
948 AssertRC(vrc); RT_NOREF(vrc);
949 }
[1]950 }
951 else
[24830]952 RTPrintf("\nStarting event loop....\n[press Ctrl-C to quit]\n");
[1]953
[24828]954 if (g_pszPidFile)
[1]955 {
[24830]956 RTFILE hPidFile = NIL_RTFILE;
957 vrc = RTFileOpen(&hPidFile, g_pszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
[24828]958 if (RT_SUCCESS(vrc))
959 {
[85268]960 char szBuf[64];
961 size_t cchToWrite = RTStrPrintf(szBuf, sizeof(szBuf), "%ld\n", (long)getpid());
962 RTFileWrite(hPidFile, szBuf, cchToWrite, NULL);
[24830]963 RTFileClose(hPidFile);
[24828]964 }
[1]965 }
966
[16382]967 // Increase the file table size to 10240 or as high as possible.
[16370]968 struct rlimit lim;
969 if (getrlimit(RLIMIT_NOFILE, &lim) == 0)
970 {
[16382]971 if ( lim.rlim_cur < 10240
972 && lim.rlim_cur < lim.rlim_max)
[16370]973 {
[16382]974 lim.rlim_cur = RT_MIN(lim.rlim_max, 10240);
975 if (setrlimit(RLIMIT_NOFILE, &lim) == -1)
[24830]976 RTPrintf("WARNING: failed to increase file descriptor limit. (%d)\n", errno);
[16370]977 }
978 }
979 else
[24830]980 RTPrintf("WARNING: failed to obtain per-process file-descriptor limit (%d).\n", errno);
[16370]981
[58579]982 /* get the main thread's event queue */
983 gEventQ = com::NativeEventQueue::getMainEventQueue();
984 if (!gEventQ)
985 {
986 RTMsgError("Failed to get the main event queue! (rc=%Rhrc)", rc);
987 break;
988 }
989
[1]990 while (gKeepRunning)
991 {
[58579]992 vrc = gEventQ->processEventQueue(RT_INDEFINITE_WAIT);
993 if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
994 {
995 LogRel(("Failed to wait for events! (rc=%Rrc)", vrc));
996 break;
997 }
[1]998 }
999
[58579]1000 gEventQ = NULL;
1001 RTPrintf("Terminated event loop.\n");
[15604]1002
1003 /* unregister ourselves. After this point, clients will start a new
1004 * process because they won't be able to resolve the server name.*/
[24828]1005 gIpcServ->RemoveName(VBOXSVC_IPC_NAME);
[1]1006 }
1007 while (0); // this scopes the nsCOMPtrs
1008
[24828]1009 NS_IF_RELEASE(gIpcServ);
[1]1010
[2976]1011 /* no nsCOMPtrs are allowed to be alive when you call com::Shutdown(). */
[1]1012
[24828]1013 LogFlowFunc(("Calling com::Shutdown()...\n"));
[2976]1014 rc = com::Shutdown();
[24828]1015 LogFlowFunc(("Finished com::Shutdown() (rc=%Rhrc)\n", rc));
[2976]1016
[24830]1017 if (NS_FAILED(rc))
1018 RTMsgError("Failed to shutdown XPCOM! (rc=%Rhrc)", rc);
[1]1019
[103300]1020#ifdef VBOX_WITH_XPCOMIPCD_IN_VBOX_SVC
1021 vrc = VBoxXpcomIpcdDestroy(hThrdIpcd);
1022 AssertRC(vrc);
1023#endif
1024
[24830]1025 RTPrintf("XPCOM server has shutdown.\n");
[1]1026
[24828]1027 if (g_pszPidFile)
1028 RTFileDelete(g_pszPidFile);
[1]1029
[61196]1030 return RTEXITCODE_SUCCESS;
[1]1031}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use