VirtualBox

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

Last change on this file was 103331, checked in by vboxsync, 3 months ago

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

© 2023 Oracle
ContactPrivacy policyTerms of Use