VirtualBox

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

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

Copyright year updates by scm.

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