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, 3 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
Line 
1/* $Id: server.cpp 103300 2024-02-11 20:33:57Z vboxsync $ */
2/** @file
3 * XPCOM server process (VBoxSVC) start point.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
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
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_VBOXSVC
29#include <ipcIService.h>
30#include <ipcCID.h>
31
32#include <nsIComponentRegistrar.h>
33
34#include <nsGenericFactory.h>
35
36#include "server.h"
37
38#include "LoggingNew.h"
39
40#include <VBox/param.h>
41#include <VBox/version.h>
42
43#include <iprt/buildconfig.h>
44#include <iprt/initterm.h>
45#include <iprt/critsect.h>
46#include <iprt/getopt.h>
47#include <iprt/message.h>
48#include <iprt/string.h>
49#include <iprt/stream.h>
50#include <iprt/path.h>
51#include <iprt/pipe.h>
52#include <iprt/process.h>
53#include <iprt/timer.h>
54#include <iprt/env.h>
55
56#include <signal.h> // for the signal handler
57#include <stdlib.h>
58#include <unistd.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <sys/stat.h>
62#include <sys/resource.h>
63
64/////////////////////////////////////////////////////////////////////////////
65// VirtualBox component instantiation
66/////////////////////////////////////////////////////////////////////////////
67
68#include <nsIGenericFactory.h>
69#include <VBox/com/VirtualBox.h>
70
71#include "VBox/com/NativeEventQueue.h"
72
73#include "ApplianceImpl.h"
74#include "AudioAdapterImpl.h"
75#include "BandwidthControlImpl.h"
76#include "BandwidthGroupImpl.h"
77#include "NetworkServiceRunner.h"
78#include "DHCPServerImpl.h"
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"
95#include "USBDeviceFiltersImpl.h"
96#include "VFSExplorerImpl.h"
97#include "VirtualBoxImpl.h"
98#include "VRDEServerImpl.h"
99#ifdef VBOX_WITH_USB
100# include "HostUSBDeviceImpl.h"
101# include "USBDeviceFilterImpl.h"
102# include "USBDeviceImpl.h"
103#endif
104#ifdef VBOX_WITH_EXTPACK
105# include "ExtPackManagerImpl.h"
106#endif
107# include "NATNetworkImpl.h"
108
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)
113
114////////////////////////////////////////////////////////////////////////////////
115
116static bool gAutoShutdown = false;
117/** Delay before shutting down the VirtualBox server after the last
118 * VirtualBox instance is released, in ms */
119static uint32_t gShutdownDelayMs = 5000;
120
121static com::NativeEventQueue *gEventQ = NULL;
122static PRBool volatile gKeepRunning = PR_TRUE;
123static PRBool volatile gAllowSigUsrQuit = PR_TRUE;
124
125/////////////////////////////////////////////////////////////////////////////
126
127/**
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 {
138 LogFlowFunc(("Deleting VirtualBox...\n"));
139
140 FinalRelease();
141 sInstance = NULL;
142
143 LogFlowFunc(("VirtualBox object deleted.\n"));
144 RTPrintf("Informational: VirtualBox object deleted.\n");
145 }
146
147 NS_IMETHOD_(nsrefcnt) Release()
148 {
149 /* we overload Release() to guarantee the VirtualBox destructor is
150 * always called on the main thread */
151
152 nsrefcnt count = VirtualBox::Release();
153
154 if (count == 1)
155 {
156 /* the last reference held by clients is being released
157 * (see GetInstance()) */
158
159 bool onMainThread = RTThreadIsMain(RTThreadSelf());
160 PRBool timerStarted = PR_FALSE;
161
162 /* sTimer is null if this call originates from FactoryDestructor()*/
163 if (sTimer != NULL)
164 {
165 LogFlowFunc(("Last VirtualBox instance was released.\n"));
166 LogFlowFunc(("Scheduling server shutdown in %u ms...\n",
167 gShutdownDelayMs));
168
169 /* make sure the previous timer (if any) is stopped;
170 * otherwise RTTimerStart() will definitely fail. */
171 RTTimerLRStop(sTimer);
172
173 int vrc = RTTimerLRStart(sTimer, gShutdownDelayMs * RT_NS_1MS_64);
174 AssertRC(vrc);
175 timerStarted = RT_BOOL(RT_SUCCESS(vrc));
176 }
177 else
178 {
179 LogFlowFunc(("Last VirtualBox instance was released "
180 "on XPCOM shutdown.\n"));
181 Assert(onMainThread);
182 }
183
184 gAllowSigUsrQuit = PR_TRUE;
185
186 if (!timerStarted)
187 {
188 if (!onMainThread)
189 {
190 /* Failed to start the timer, post the shutdown event
191 * manually if not on the main thread already. */
192 ShutdownTimer(NULL, NULL, 0);
193 }
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 *
207 * In either case, there is nothing to do.
208 *
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.
212 */
213
214 Assert(!gEventQ);
215 }
216 }
217 }
218
219 return count;
220 }
221
222 class MaybeQuitEvent : public NativeEvent
223 {
224 public:
225 MaybeQuitEvent() :
226 m_fSignal(false)
227 {
228 }
229
230 MaybeQuitEvent(bool fSignal) :
231 m_fSignal(fSignal)
232 {
233 }
234
235 private:
236 /* called on the main thread */
237 void *handler()
238 {
239 LogFlowFuncEnter();
240
241 Assert(RTCritSectIsInitialized(&sLock));
242
243 /* stop accepting GetInstance() requests on other threads during
244 * possible destruction */
245 RTCritSectEnter(&sLock);
246
247 nsrefcnt count = 1;
248
249 /* sInstance is NULL here if it was deleted immediately after
250 * creation due to initialization error. See GetInstance(). */
251 if (sInstance != NULL)
252 {
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();
259 count = sInstance->Release();
260 if (count == 1)
261 {
262 RTTimerLRStop(sTimer);
263 /* Release the guard reference added in GetInstance() */
264 sInstance->Release();
265 }
266 }
267
268 if (count == 1)
269 {
270 if (gAutoShutdown || m_fSignal)
271 {
272 Assert(sInstance == NULL);
273 LogFlowFunc(("Terminating the server process...\n"));
274 /* make it leave the event loop */
275 gKeepRunning = PR_FALSE;
276 }
277 else
278 LogFlowFunc(("No automatic shutdown.\n"));
279 }
280 else
281 {
282 /* This condition is quite rare: a new client happened to
283 * connect after this event has been posted to the main queue
284 * but before it started to process it. */
285 LogRel(("Destruction is canceled (refcnt=%d).\n", count));
286 }
287
288 RTCritSectLeave(&sLock);
289
290 LogFlowFuncLeave();
291 return NULL;
292 }
293
294 bool m_fSignal;
295 };
296
297 static DECLCALLBACK(void) ShutdownTimer(RTTIMERLR hTimerLR, void *pvUser, uint64_t /*iTick*/)
298 {
299 NOREF(hTimerLR);
300 NOREF(pvUser);
301
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. */
306 com::NativeEventQueue *q = gEventQ;
307 AssertReturnVoid(q);
308
309 /* post a quit event to the main queue */
310 MaybeQuitEvent *ev = new MaybeQuitEvent(false /* fSignal */);
311 if (!q->postEvent(ev))
312 delete ev;
313
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. */
317 }
318
319 static NS_IMETHODIMP FactoryConstructor()
320 {
321 LogFlowFunc(("\n"));
322
323 /* create a critsect to protect object construction */
324 if (RT_FAILURE(RTCritSectInit(&sLock)))
325 return NS_ERROR_OUT_OF_MEMORY;
326
327 int vrc = RTTimerLRCreateEx(&sTimer, 0, 0, ShutdownTimer, NULL);
328 if (RT_FAILURE(vrc))
329 {
330 LogFlowFunc(("Failed to create a timer! (vrc=%Rrc)\n", vrc));
331 return NS_ERROR_FAILURE;
332 }
333
334 return NS_OK;
335 }
336
337 static NS_IMETHODIMP FactoryDestructor()
338 {
339 LogFlowFunc(("\n"));
340
341 RTTimerLRDestroy(sTimer);
342 sTimer = NULL;
343
344 if (sInstance != NULL)
345 {
346 /* Either posting a destruction event failed for some reason (most
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).
350 * Release the guard reference we added in GetInstance(). */
351 sInstance->Release();
352 }
353
354 /* Destroy lock after releasing the VirtualBox instance, otherwise
355 * there are races with cleanup. */
356 RTCritSectDelete(&sLock);
357
358 return NS_OK;
359 }
360
361 static nsresult GetInstance(VirtualBox **inst)
362 {
363 LogFlowFunc(("Getting VirtualBox object...\n"));
364
365 RTCritSectEnter(&sLock);
366
367 if (!gKeepRunning)
368 {
369 LogFlowFunc(("Process termination requested first. Refusing.\n"));
370
371 RTCritSectLeave(&sLock);
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)
383 {
384 LogFlowFunc(("Creating new VirtualBox object...\n"));
385 sInstance = new VirtualBoxClassFactory();
386 if (sInstance != NULL)
387 {
388 /* make an extra AddRef to take the full control
389 * on the VirtualBox destruction (see FinalRelease()) */
390 sInstance->AddRef();
391
392 sInstance->AddRef(); /* protect FinalConstruct() */
393 rv = sInstance->FinalConstruct();
394 RTPrintf("Informational: VirtualBox object created (rc=%Rhrc).\n", rv);
395 if (NS_FAILED(rv))
396 {
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,
403 * the main reason to maintain the shutdown delay on
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();
408 Assert(sInstance == NULL);
409 }
410 else
411 {
412 /* On success, make sure the previous timer is stopped to
413 * cancel a scheduled server termination (if any). */
414 gAllowSigUsrQuit = PR_FALSE;
415 RTTimerLRStop(sTimer);
416 }
417 }
418 else
419 {
420 rv = NS_ERROR_OUT_OF_MEMORY;
421 }
422 }
423 else
424 {
425 LogFlowFunc(("Using existing VirtualBox object...\n"));
426 nsrefcnt count = sInstance->AddRef();
427 Assert(count > 1);
428
429 if (count >= 2)
430 {
431 LogFlowFunc(("Another client has requested a reference to VirtualBox, canceling destruction...\n"));
432
433 /* make sure the previous timer is stopped */
434 gAllowSigUsrQuit = PR_FALSE;
435 RTTimerLRStop(sTimer);
436 }
437 }
438
439 *inst = sInstance;
440
441 RTCritSectLeave(&sLock);
442
443 return rv;
444 }
445
446private:
447
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;
455 static RTCRITSECT sLock;
456
457 static RTTIMERLR sTimer;
458};
459
460VirtualBoxClassFactory *VirtualBoxClassFactory::sInstance = NULL;
461RTCRITSECT VirtualBoxClassFactory::sLock;
462
463RTTIMERLR VirtualBoxClassFactory::sTimer = NIL_RTTIMERLR;
464
465NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR_WITH_RC(VirtualBox, VirtualBoxClassFactory::GetInstance)
466
467////////////////////////////////////////////////////////////////////////////////
468
469typedef NSFactoryDestructorProcPtr NSFactoryConstructorProcPtr;
470
471/**
472 * Enhanced module component information structure.
473 *
474 * nsModuleComponentInfo lacks the factory construction callback, here we add
475 * it. This callback is called straight after a nsGenericFactory instance is
476 * successfully created in RegisterSelfComponents.
477 */
478struct nsModuleComponentInfoPlusFactoryConstructor
479{
480 /** standard module component information */
481 const nsModuleComponentInfo *mpModuleComponentInfo;
482 /** (optional) Factory Construction Callback */
483 NSFactoryConstructorProcPtr mFactoryConstructor;
484};
485
486/////////////////////////////////////////////////////////////////////////////
487
488/**
489 * Helper function to register self components upon start-up
490 * of the out-of-proc server.
491 */
492static nsresult
493RegisterSelfComponents(nsIComponentRegistrar *registrar,
494 const nsModuleComponentInfoPlusFactoryConstructor *aComponents,
495 PRUint32 count)
496{
497 nsresult rc = NS_OK;
498 const nsModuleComponentInfoPlusFactoryConstructor *info = aComponents;
499 for (PRUint32 i = 0; i < count && NS_SUCCEEDED(rc); i++, info++)
500 {
501 /* skip components w/o a constructor */
502 if (!info->mpModuleComponentInfo->mConstructor)
503 continue;
504 /* create a new generic factory for a component and register it */
505 nsIGenericFactory *factory;
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 }
513 if (NS_SUCCEEDED(rc))
514 {
515 rc = registrar->RegisterFactory(info->mpModuleComponentInfo->mCID,
516 info->mpModuleComponentInfo->mDescription,
517 info->mpModuleComponentInfo->mContractID,
518 factory);
519 NS_RELEASE(factory);
520 }
521 }
522 return rc;
523}
524
525/////////////////////////////////////////////////////////////////////////////
526
527static ipcIService *gIpcServ = nsnull;
528static const char *g_pszPidFile = NULL;
529
530class ForceQuitEvent : public NativeEvent
531{
532 void *handler()
533 {
534 LogFlowFunc(("\n"));
535
536 gKeepRunning = PR_FALSE;
537
538 if (g_pszPidFile)
539 RTFileDelete(g_pszPidFile);
540
541 return NULL;
542 }
543};
544
545static void signal_handler(int sig)
546{
547 com::NativeEventQueue *q = gEventQ;
548 if (q && gKeepRunning)
549 {
550 if (sig == SIGUSR1)
551 {
552 if (gAllowSigUsrQuit)
553 {
554 /* terminate the server process if it is idle */
555 VirtualBoxClassFactory::MaybeQuitEvent *ev = new VirtualBoxClassFactory::MaybeQuitEvent(true /* fSignal */);
556 if (!q->postEvent(ev))
557 delete ev;
558 }
559 /* else do nothing */
560 }
561 else
562 {
563 /* post a force quit event to the queue */
564 ForceQuitEvent *ev = new ForceQuitEvent();
565 if (!q->postEvent(ev))
566 delete ev;
567 }
568 }
569}
570
571static nsresult vboxsvcSpawnDaemonByReExec(const char *pszPath, bool fAutoShutdown, const char *pszPidFile)
572{
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))
583 {
584 char szPipeInheritFd[32]; RT_ZERO(szPipeInheritFd);
585
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;
599
600 ssize_t cch = RTStrFormatU32(&szPipeInheritFd[0], sizeof(szPipeInheritFd),
601 (uint32_t)RTPipeToNative(hPipeWr), 10 /*uiBase*/,
602 0 /*cchWidth*/, 0 /*cchPrecision*/, 0 /*fFlags*/);
603 Assert(cch > 0); RT_NOREF(cch);
604
605 RTHANDLE hStdNil;
606 hStdNil.enmType = RTHANDLETYPE_FILE;
607 hStdNil.u.hFile = NIL_RTFILE;
608
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;
617
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 }
630
631 if (hPipeWr != NIL_RTPIPE)
632 RTPipeClose(hPipeWr);
633 RTPipeClose(hPipeRd);
634 }
635
636 return NS_ERROR_FAILURE;
637}
638
639static void showUsage(const char *pcszFileName)
640{
641 RTPrintf(VBOX_PRODUCT " VBoxSVC "
642 VBOX_VERSION_STRING "\n"
643 "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
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");
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");
662
663 RTPrintf("\n");
664}
665
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
674int main(int argc, char **argv)
675{
676 /*
677 * Initialize the VBox runtime without loading
678 * the support driver
679 */
680 int vrc = RTR3InitExe(argc, &argv, 0);
681 if (RT_FAILURE(vrc))
682 return RTMsgInitFailure(vrc);
683
684 static const RTGETOPTDEF s_aOptions[] =
685 {
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 }
697 };
698
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
703 uint32_t uStartupPipeFd = UINT32_MAX;
704 bool fDaemonize = false;
705
706 RTGETOPTSTATE GetOptState;
707 vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
708 AssertRC(vrc);
709
710 RTGETOPTUNION ValueUnion;
711 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
712 {
713 switch (vrc)
714 {
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
723 case 'A':
724 /* --auto-shutdown mode means we're already daemonized. */
725 gAutoShutdown = true;
726 break;
727
728 case 'd':
729 fDaemonize = true;
730 break;
731
732 case 'D':
733 gShutdownDelayMs = ValueUnion.u32;
734 break;
735
736 case 'p':
737 g_pszPidFile = ValueUnion.psz;
738 break;
739
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
756 case 'P':
757 uStartupPipeFd = ValueUnion.u32;
758 break;
759
760 case 'h':
761 showUsage(argv[0]);
762 return RTEXITCODE_SYNTAX;
763
764 case 'V':
765 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
766 return RTEXITCODE_SUCCESS;
767
768 default:
769 return RTGetOptPrintError(vrc, &ValueUnion);
770 }
771 }
772
773 if (fDaemonize)
774 {
775 vboxsvcSpawnDaemonByReExec(argv[0], gAutoShutdown, g_pszPidFile);
776 exit(126);
777 }
778
779 nsresult rc;
780
781 /** @todo Merge this code with svcmain.cpp (use Logging.cpp?). */
782 char szLogFile[RTPATH_MAX];
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 }
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
797 RTERRINFOSTATIC ErrInfo;
798 vrc = com::VBoxLogRelCreate("XPCOM Server", szLogFile,
799 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
800 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
801 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
802 cHistory, uHistoryFileTime, uHistoryFileSize,
803 RTErrInfoInitStatic(&ErrInfo));
804 if (RT_FAILURE(vrc))
805 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
806
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
813 const nsModuleComponentInfo VirtualBoxInfo = {
814 "VirtualBox component",
815 NS_VIRTUALBOX_CID,
816 NS_VIRTUALBOX_CONTRACTID,
817 VirtualBoxConstructor, // constructor function
818 NULL, // registration function
819 NULL, // deregistration function
820 VirtualBoxClassFactory::FactoryDestructor, // factory destructor function
821 NS_CI_INTERFACE_GETTER_NAME(VirtualBoxWrap),
822 NULL, // language helper
823 &NS_CLASSINFO_NAME(VirtualBoxWrap),
824 0 // flags
825 };
826
827 const nsModuleComponentInfoPlusFactoryConstructor components[] = {
828 {
829 &VirtualBoxInfo,
830 VirtualBoxClassFactory::FactoryConstructor // factory constructor function
831 }
832 };
833
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
841 do /* goto avoidance only */
842 {
843 rc = com::Initialize();
844 if (NS_FAILED(rc))
845 {
846 RTMsgError("Failed to initialize XPCOM! (rc=%Rhrc)\n", rc);
847 break;
848 }
849
850 nsCOMPtr<nsIComponentRegistrar> registrar;
851 rc = NS_GetComponentRegistrar(getter_AddRefs(registrar));
852 if (NS_FAILED(rc))
853 {
854 RTMsgError("Failed to get component registrar! (rc=%Rhrc)", rc);
855 break;
856 }
857
858 registrar->AutoRegister(nsnull);
859 rc = RegisterSelfComponents(registrar, components,
860 NS_ARRAY_LENGTH(components));
861 if (NS_FAILED(rc))
862 {
863 RTMsgError("Failed to register server components! (rc=%Rhrc)", rc);
864 break;
865 }
866
867 nsCOMPtr<ipcIService> ipcServ(do_GetService(IPC_SERVICE_CONTRACTID, &rc));
868 if (NS_FAILED(rc))
869 {
870 RTMsgError("Failed to get IPC service! (rc=%Rhrc)", rc);
871 break;
872 }
873
874 NS_ADDREF(gIpcServ = ipcServ);
875
876 LogFlowFunc(("Will use \"%s\" as server name.\n", VBOXSVC_IPC_NAME));
877
878 rc = gIpcServ->AddName(VBOXSVC_IPC_NAME);
879 if (NS_FAILED(rc))
880 {
881 LogFlowFunc(("Failed to register the server name (rc=%Rhrc (%08X))!\n"
882 "Is another server already running?\n", rc, rc));
883
884 RTMsgError("Failed to register the server name \"%s\" (rc=%Rhrc)!\n"
885 "Is another server already running?\n",
886 VBOXSVC_IPC_NAME, rc);
887 NS_RELEASE(gIpcServ);
888 break;
889 }
890
891 {
892 /* setup signal handling to convert some signals to a quit event */
893 struct sigaction sa;
894 sa.sa_handler = signal_handler;
895 sigemptyset(&sa.sa_mask);
896 sa.sa_flags = 0;
897 sigaction(SIGINT, &sa, NULL);
898 sigaction(SIGQUIT, &sa, NULL);
899 sigaction(SIGTERM, &sa, NULL);
900// XXX Temporary allow release assertions to terminate VBoxSVC
901// sigaction(SIGTRAP, &sa, NULL);
902 sigaction(SIGUSR1, &sa, NULL);
903 }
904
905 {
906 char szBuf[80];
907 size_t cSize;
908
909 cSize = RTStrPrintf(szBuf, sizeof(szBuf),
910 VBOX_PRODUCT" XPCOM Server Version "
911 VBOX_VERSION_STRING);
912 for (size_t i = cSize; i > 0; i--)
913 putchar('*');
914 RTPrintf("\n%s\n", szBuf);
915 RTPrintf("Copyright (C) 2004-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
916#ifdef DEBUG
917 RTPrintf("Debug version.\n");
918#endif
919 }
920
921 if (uStartupPipeFd == UINT32_MAX)
922 {
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 {
938 RTPrintf("\nStarting event loop....\n[send TERM signal to quit]\n");
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 }
950 }
951 else
952 RTPrintf("\nStarting event loop....\n[press Ctrl-C to quit]\n");
953
954 if (g_pszPidFile)
955 {
956 RTFILE hPidFile = NIL_RTFILE;
957 vrc = RTFileOpen(&hPidFile, g_pszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
958 if (RT_SUCCESS(vrc))
959 {
960 char szBuf[64];
961 size_t cchToWrite = RTStrPrintf(szBuf, sizeof(szBuf), "%ld\n", (long)getpid());
962 RTFileWrite(hPidFile, szBuf, cchToWrite, NULL);
963 RTFileClose(hPidFile);
964 }
965 }
966
967 // Increase the file table size to 10240 or as high as possible.
968 struct rlimit lim;
969 if (getrlimit(RLIMIT_NOFILE, &lim) == 0)
970 {
971 if ( lim.rlim_cur < 10240
972 && lim.rlim_cur < lim.rlim_max)
973 {
974 lim.rlim_cur = RT_MIN(lim.rlim_max, 10240);
975 if (setrlimit(RLIMIT_NOFILE, &lim) == -1)
976 RTPrintf("WARNING: failed to increase file descriptor limit. (%d)\n", errno);
977 }
978 }
979 else
980 RTPrintf("WARNING: failed to obtain per-process file-descriptor limit (%d).\n", errno);
981
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
990 while (gKeepRunning)
991 {
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 }
998 }
999
1000 gEventQ = NULL;
1001 RTPrintf("Terminated event loop.\n");
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.*/
1005 gIpcServ->RemoveName(VBOXSVC_IPC_NAME);
1006 }
1007 while (0); // this scopes the nsCOMPtrs
1008
1009 NS_IF_RELEASE(gIpcServ);
1010
1011 /* no nsCOMPtrs are allowed to be alive when you call com::Shutdown(). */
1012
1013 LogFlowFunc(("Calling com::Shutdown()...\n"));
1014 rc = com::Shutdown();
1015 LogFlowFunc(("Finished com::Shutdown() (rc=%Rhrc)\n", rc));
1016
1017 if (NS_FAILED(rc))
1018 RTMsgError("Failed to shutdown XPCOM! (rc=%Rhrc)", rc);
1019
1020#ifdef VBOX_WITH_XPCOMIPCD_IN_VBOX_SVC
1021 vrc = VBoxXpcomIpcdDestroy(hThrdIpcd);
1022 AssertRC(vrc);
1023#endif
1024
1025 RTPrintf("XPCOM server has shutdown.\n");
1026
1027 if (g_pszPidFile)
1028 RTFileDelete(g_pszPidFile);
1029
1030 return RTEXITCODE_SUCCESS;
1031}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use