VirtualBox

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

Last change on this file since 102048 was 102048, checked in by vboxsync, 12 months ago

Main/XPCOM: Convert re-spawning VBoxSVC from NSPR to IPRT, bugref:10545

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.8 KB
Line 
1/* $Id: server.cpp 102048 2023-11-09 19:47:10Z 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);
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
666int main(int argc, char **argv)
667{
668 /*
669 * Initialize the VBox runtime without loading
670 * the support driver
671 */
672 int vrc = RTR3InitExe(argc, &argv, 0);
673 if (RT_FAILURE(vrc))
674 return RTMsgInitFailure(vrc);
675
676 static const RTGETOPTDEF s_aOptions[] =
677 {
678 { "--automate", 'a', RTGETOPT_REQ_NOTHING },
679 { "--auto-shutdown", 'A', RTGETOPT_REQ_NOTHING },
680 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
681 { "--help", 'h', RTGETOPT_REQ_NOTHING },
682 { "--shutdown-delay", 'D', RTGETOPT_REQ_UINT32 },
683 { "--pidfile", 'p', RTGETOPT_REQ_STRING },
684 { "--logfile", 'F', RTGETOPT_REQ_STRING },
685 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
686 { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
687 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 },
688 { "--inherit-startup-pipe", 'P', RTGETOPT_REQ_UINT32 }
689 };
690
691 const char *pszLogFile = NULL;
692 uint32_t cHistory = 10; // enable log rotation, 10 files
693 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
694 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
695 uint32_t uStartupPipeFd = UINT32_MAX;
696 bool fDaemonize = false;
697
698 RTGETOPTSTATE GetOptState;
699 vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
700 AssertRC(vrc);
701
702 RTGETOPTUNION ValueUnion;
703 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
704 {
705 switch (vrc)
706 {
707 case 'a':
708 /* --automate mode means we are started by XPCOM on
709 * demand. Daemonize ourselves and activate
710 * auto-shutdown. */
711 gAutoShutdown = true;
712 fDaemonize = true;
713 break;
714
715 case 'A':
716 /* --auto-shutdown mode means we're already daemonized. */
717 gAutoShutdown = true;
718 break;
719
720 case 'd':
721 fDaemonize = true;
722 break;
723
724 case 'D':
725 gShutdownDelayMs = ValueUnion.u32;
726 break;
727
728 case 'p':
729 g_pszPidFile = ValueUnion.psz;
730 break;
731
732 case 'F':
733 pszLogFile = ValueUnion.psz;
734 break;
735
736 case 'R':
737 cHistory = ValueUnion.u32;
738 break;
739
740 case 'S':
741 uHistoryFileSize = ValueUnion.u64;
742 break;
743
744 case 'I':
745 uHistoryFileTime = ValueUnion.u32;
746 break;
747
748 case 'P':
749 uStartupPipeFd = ValueUnion.u32;
750 break;
751
752 case 'h':
753 showUsage(argv[0]);
754 return RTEXITCODE_SYNTAX;
755
756 case 'V':
757 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
758 return RTEXITCODE_SUCCESS;
759
760 default:
761 return RTGetOptPrintError(vrc, &ValueUnion);
762 }
763 }
764
765 if (fDaemonize)
766 {
767 vboxsvcSpawnDaemonByReExec(argv[0], gAutoShutdown, g_pszPidFile);
768 exit(126);
769 }
770
771 nsresult rc;
772
773 /** @todo Merge this code with svcmain.cpp (use Logging.cpp?). */
774 char szLogFile[RTPATH_MAX];
775 if (!pszLogFile)
776 {
777 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
778 if (RT_SUCCESS(vrc))
779 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
780 }
781 else
782 {
783 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
784 vrc = VERR_NO_MEMORY;
785 }
786 if (RT_FAILURE(vrc))
787 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
788
789 RTERRINFOSTATIC ErrInfo;
790 vrc = com::VBoxLogRelCreate("XPCOM Server", szLogFile,
791 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
792 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
793 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
794 cHistory, uHistoryFileTime, uHistoryFileSize,
795 RTErrInfoInitStatic(&ErrInfo));
796 if (RT_FAILURE(vrc))
797 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
798
799 /* Set up a build identifier so that it can be seen from core dumps what
800 * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
801 static char saBuildID[48];
802 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
803 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
804
805 const nsModuleComponentInfo VirtualBoxInfo = {
806 "VirtualBox component",
807 NS_VIRTUALBOX_CID,
808 NS_VIRTUALBOX_CONTRACTID,
809 VirtualBoxConstructor, // constructor function
810 NULL, // registration function
811 NULL, // deregistration function
812 VirtualBoxClassFactory::FactoryDestructor, // factory destructor function
813 NS_CI_INTERFACE_GETTER_NAME(VirtualBoxWrap),
814 NULL, // language helper
815 &NS_CLASSINFO_NAME(VirtualBoxWrap),
816 0 // flags
817 };
818
819 const nsModuleComponentInfoPlusFactoryConstructor components[] = {
820 {
821 &VirtualBoxInfo,
822 VirtualBoxClassFactory::FactoryConstructor // factory constructor function
823 }
824 };
825
826 do /* goto avoidance only */
827 {
828 rc = com::Initialize();
829 if (NS_FAILED(rc))
830 {
831 RTMsgError("Failed to initialize XPCOM! (rc=%Rhrc)\n", rc);
832 break;
833 }
834
835 nsCOMPtr<nsIComponentRegistrar> registrar;
836 rc = NS_GetComponentRegistrar(getter_AddRefs(registrar));
837 if (NS_FAILED(rc))
838 {
839 RTMsgError("Failed to get component registrar! (rc=%Rhrc)", rc);
840 break;
841 }
842
843 registrar->AutoRegister(nsnull);
844 rc = RegisterSelfComponents(registrar, components,
845 NS_ARRAY_LENGTH(components));
846 if (NS_FAILED(rc))
847 {
848 RTMsgError("Failed to register server components! (rc=%Rhrc)", rc);
849 break;
850 }
851
852 nsCOMPtr<ipcIService> ipcServ(do_GetService(IPC_SERVICE_CONTRACTID, &rc));
853 if (NS_FAILED(rc))
854 {
855 RTMsgError("Failed to get IPC service! (rc=%Rhrc)", rc);
856 break;
857 }
858
859 NS_ADDREF(gIpcServ = ipcServ);
860
861 LogFlowFunc(("Will use \"%s\" as server name.\n", VBOXSVC_IPC_NAME));
862
863 rc = gIpcServ->AddName(VBOXSVC_IPC_NAME);
864 if (NS_FAILED(rc))
865 {
866 LogFlowFunc(("Failed to register the server name (rc=%Rhrc (%08X))!\n"
867 "Is another server already running?\n", rc, rc));
868
869 RTMsgError("Failed to register the server name \"%s\" (rc=%Rhrc)!\n"
870 "Is another server already running?\n",
871 VBOXSVC_IPC_NAME, rc);
872 NS_RELEASE(gIpcServ);
873 break;
874 }
875
876 {
877 /* setup signal handling to convert some signals to a quit event */
878 struct sigaction sa;
879 sa.sa_handler = signal_handler;
880 sigemptyset(&sa.sa_mask);
881 sa.sa_flags = 0;
882 sigaction(SIGINT, &sa, NULL);
883 sigaction(SIGQUIT, &sa, NULL);
884 sigaction(SIGTERM, &sa, NULL);
885// XXX Temporary allow release assertions to terminate VBoxSVC
886// sigaction(SIGTRAP, &sa, NULL);
887 sigaction(SIGUSR1, &sa, NULL);
888 }
889
890 {
891 char szBuf[80];
892 size_t cSize;
893
894 cSize = RTStrPrintf(szBuf, sizeof(szBuf),
895 VBOX_PRODUCT" XPCOM Server Version "
896 VBOX_VERSION_STRING);
897 for (size_t i = cSize; i > 0; i--)
898 putchar('*');
899 RTPrintf("\n%s\n", szBuf);
900 RTPrintf("Copyright (C) 2004-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
901#ifdef DEBUG
902 RTPrintf("Debug version.\n");
903#endif
904 }
905
906 if (uStartupPipeFd == UINT32_MAX)
907 {
908 /* Check the environment variable. */
909 const char *pszStartupPipe = RTEnvGet("VBOX_STARTUP_PIPE_FD");
910 if (pszStartupPipe)
911 {
912 /* Convert it to a number. */
913 vrc = RTStrToUInt32Full(pszStartupPipe, 0, &uStartupPipeFd);
914 if (RT_FAILURE(vrc))
915 {
916 RTMsgError("Failed to parse VBOX_STARTUP_PIPE_FD=%s! (rc=%Rrc)", pszStartupPipe, vrc);
917 break;
918 }
919 }
920 }
921 if (uStartupPipeFd != UINT32_MAX)
922 {
923 RTPrintf("\nStarting event loop....\n[send TERM signal to quit]\n");
924
925 RTPIPE hPipe = NIL_RTPIPE;
926 vrc = RTPipeFromNative(&hPipe, (RTHCINTPTR)uStartupPipeFd, RTPIPE_N_WRITE);
927 if (RT_SUCCESS(vrc))
928 {
929 vrc = RTPipeWriteBlocking(hPipe, RT_STR_TUPLE("READY"), NULL /*pcbWritten*/);
930 AssertRC(vrc); RT_NOREF(vrc);
931
932 vrc = RTPipeClose(hPipe);
933 AssertRC(vrc); RT_NOREF(vrc);
934 }
935 }
936 else
937 RTPrintf("\nStarting event loop....\n[press Ctrl-C to quit]\n");
938
939 if (g_pszPidFile)
940 {
941 RTFILE hPidFile = NIL_RTFILE;
942 vrc = RTFileOpen(&hPidFile, g_pszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
943 if (RT_SUCCESS(vrc))
944 {
945 char szBuf[64];
946 size_t cchToWrite = RTStrPrintf(szBuf, sizeof(szBuf), "%ld\n", (long)getpid());
947 RTFileWrite(hPidFile, szBuf, cchToWrite, NULL);
948 RTFileClose(hPidFile);
949 }
950 }
951
952 // Increase the file table size to 10240 or as high as possible.
953 struct rlimit lim;
954 if (getrlimit(RLIMIT_NOFILE, &lim) == 0)
955 {
956 if ( lim.rlim_cur < 10240
957 && lim.rlim_cur < lim.rlim_max)
958 {
959 lim.rlim_cur = RT_MIN(lim.rlim_max, 10240);
960 if (setrlimit(RLIMIT_NOFILE, &lim) == -1)
961 RTPrintf("WARNING: failed to increase file descriptor limit. (%d)\n", errno);
962 }
963 }
964 else
965 RTPrintf("WARNING: failed to obtain per-process file-descriptor limit (%d).\n", errno);
966
967 /* get the main thread's event queue */
968 gEventQ = com::NativeEventQueue::getMainEventQueue();
969 if (!gEventQ)
970 {
971 RTMsgError("Failed to get the main event queue! (rc=%Rhrc)", rc);
972 break;
973 }
974
975 while (gKeepRunning)
976 {
977 vrc = gEventQ->processEventQueue(RT_INDEFINITE_WAIT);
978 if (RT_FAILURE(vrc) && vrc != VERR_TIMEOUT)
979 {
980 LogRel(("Failed to wait for events! (rc=%Rrc)", vrc));
981 break;
982 }
983 }
984
985 gEventQ = NULL;
986 RTPrintf("Terminated event loop.\n");
987
988 /* unregister ourselves. After this point, clients will start a new
989 * process because they won't be able to resolve the server name.*/
990 gIpcServ->RemoveName(VBOXSVC_IPC_NAME);
991 }
992 while (0); // this scopes the nsCOMPtrs
993
994 NS_IF_RELEASE(gIpcServ);
995
996 /* no nsCOMPtrs are allowed to be alive when you call com::Shutdown(). */
997
998 LogFlowFunc(("Calling com::Shutdown()...\n"));
999 rc = com::Shutdown();
1000 LogFlowFunc(("Finished com::Shutdown() (rc=%Rhrc)\n", rc));
1001
1002 if (NS_FAILED(rc))
1003 RTMsgError("Failed to shutdown XPCOM! (rc=%Rhrc)", rc);
1004
1005 RTPrintf("XPCOM server has shutdown.\n");
1006
1007 if (g_pszPidFile)
1008 RTFileDelete(g_pszPidFile);
1009
1010 return RTEXITCODE_SUCCESS;
1011}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette