VirtualBox

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

Last change on this file since 55747 was 55458, checked in by vboxsync, 10 years ago

Main/src-server/xpcom/server.cpp: make the event queue handling and the critsect destruction a bit more robust

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.6 KB
Line 
1/* $Id: server.cpp 55458 2015-04-27 16:38:40Z vboxsync $ */
2/** @file
3 * XPCOM server process (VBoxSVC) start point.
4 */
5
6/*
7 * Copyright (C) 2004-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <ipcIService.h>
19#include <ipcCID.h>
20
21#include <nsIComponentRegistrar.h>
22
23#include <nsEventQueueUtils.h>
24#include <nsGenericFactory.h>
25
26#include "prio.h"
27#include "prproces.h"
28
29#include "server.h"
30
31#include "Logging.h"
32
33#include <VBox/param.h>
34
35#include <iprt/buildconfig.h>
36#include <iprt/initterm.h>
37#include <iprt/critsect.h>
38#include <iprt/getopt.h>
39#include <iprt/message.h>
40#include <iprt/string.h>
41#include <iprt/stream.h>
42#include <iprt/path.h>
43#include <iprt/timer.h>
44#include <iprt/env.h>
45
46#include <signal.h> // for the signal handler
47#include <stdlib.h>
48#include <unistd.h>
49#include <errno.h>
50#include <fcntl.h>
51#include <sys/stat.h>
52#include <sys/resource.h>
53
54/////////////////////////////////////////////////////////////////////////////
55// VirtualBox component instantiation
56/////////////////////////////////////////////////////////////////////////////
57
58#include <nsIGenericFactory.h>
59#include <VirtualBox_XPCOM.h>
60
61#include "ApplianceImpl.h"
62#include "AudioAdapterImpl.h"
63#include "BandwidthControlImpl.h"
64#include "BandwidthGroupImpl.h"
65#include "NetworkServiceRunner.h"
66#include "DHCPServerImpl.h"
67#include "GuestOSTypeImpl.h"
68#include "HostImpl.h"
69#include "HostNetworkInterfaceImpl.h"
70#include "MachineImpl.h"
71#include "MediumFormatImpl.h"
72#include "MediumImpl.h"
73#include "NATEngineImpl.h"
74#include "NetworkAdapterImpl.h"
75#include "ParallelPortImpl.h"
76#include "ProgressProxyImpl.h"
77#include "SerialPortImpl.h"
78#include "SharedFolderImpl.h"
79#include "SnapshotImpl.h"
80#include "StorageControllerImpl.h"
81#include "SystemPropertiesImpl.h"
82#include "USBControllerImpl.h"
83#include "USBDeviceFiltersImpl.h"
84#include "VFSExplorerImpl.h"
85#include "VirtualBoxImpl.h"
86#include "VRDEServerImpl.h"
87#ifdef VBOX_WITH_USB
88# include "HostUSBDeviceImpl.h"
89# include "USBDeviceFilterImpl.h"
90# include "USBDeviceImpl.h"
91#endif
92#ifdef VBOX_WITH_EXTPACK
93# include "ExtPackManagerImpl.h"
94#endif
95# include "NATNetworkImpl.h"
96
97// This needs to stay - it is needed by the service registration below, and
98// is defined in the automatically generated VirtualBoxWrap.cpp
99extern nsIClassInfo *NS_CLASSINFO_NAME(VirtualBoxWrap);
100NS_DECL_CI_INTERFACE_GETTER(VirtualBoxWrap)
101
102// The declarations/implementations of the various XPCOM helper data structures
103// and functions have to be removed bit by bit, as the conversion to the
104// automatically generated wrappers makes them obsolete.
105
106/* implement nsISupports parts of our objects with support for nsIClassInfo */
107NS_DECL_CLASSINFO(SessionMachine)
108NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
109
110NS_DECL_CLASSINFO(SnapshotMachine)
111NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
112
113NS_DECL_CLASSINFO(ProgressProxy)
114NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProgressProxy, IProgress)
115
116#ifdef VBOX_WITH_USB
117
118#endif
119
120#ifdef VBOX_WITH_RESOURCE_USAGE_API
121NS_DECL_CLASSINFO(PerformanceCollector)
122NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PerformanceCollector, IPerformanceCollector)
123NS_DECL_CLASSINFO(PerformanceMetric)
124NS_IMPL_THREADSAFE_ISUPPORTS1_CI(PerformanceMetric, IPerformanceMetric)
125#endif /* VBOX_WITH_RESOURCE_USAGE_API */
126
127////////////////////////////////////////////////////////////////////////////////
128
129static bool gAutoShutdown = false;
130/** Delay before shutting down the VirtualBox server after the last
131 * VirtualBox instance is released, in ms */
132static uint32_t gShutdownDelayMs = 5000;
133
134static nsCOMPtr<nsIEventQueue> gEventQ = nsnull;
135static PRBool volatile gKeepRunning = PR_TRUE;
136static PRBool volatile gAllowSigUsrQuit = PR_TRUE;
137
138/////////////////////////////////////////////////////////////////////////////
139
140/**
141 * Simple but smart PLEvent wrapper.
142 *
143 * @note Instances must be always created with <tt>operator new</tt>!
144 */
145class MyEvent
146{
147public:
148
149 MyEvent()
150 {
151 mEv.that = NULL;
152 };
153
154 /**
155 * Posts this event to the given message queue. This method may only be
156 * called once. @note On success, the event will be deleted automatically
157 * after it is delivered and handled. On failure, the event will delete
158 * itself before this method returns! The caller must not delete it in
159 * either case.
160 */
161 nsresult postTo(nsIEventQueue *aEventQ)
162 {
163 AssertReturn(mEv.that == NULL, NS_ERROR_FAILURE);
164 AssertReturn(aEventQ, NS_ERROR_FAILURE);
165 nsresult rv = aEventQ->InitEvent(&mEv.e, NULL,
166 eventHandler, eventDestructor);
167 if (NS_SUCCEEDED(rv))
168 {
169 mEv.that = this;
170 rv = aEventQ->PostEvent(&mEv.e);
171 if (NS_SUCCEEDED(rv))
172 return rv;
173 }
174 delete this;
175 return rv;
176 }
177
178 virtual void *handler() = 0;
179
180private:
181
182 struct Ev
183 {
184 PLEvent e;
185 MyEvent *that;
186 } mEv;
187
188 static void *PR_CALLBACK eventHandler(PLEvent *self)
189 {
190 return reinterpret_cast<Ev *>(self)->that->handler();
191 }
192
193 static void PR_CALLBACK eventDestructor(PLEvent *self)
194 {
195 delete reinterpret_cast<Ev *>(self)->that;
196 }
197};
198
199////////////////////////////////////////////////////////////////////////////////
200
201/**
202 * VirtualBox class factory that destroys the created instance right after
203 * the last reference to it is released by the client, and recreates it again
204 * when necessary (so VirtualBox acts like a singleton object).
205 */
206class VirtualBoxClassFactory : public VirtualBox
207{
208public:
209
210 virtual ~VirtualBoxClassFactory()
211 {
212 LogFlowFunc(("Deleting VirtualBox...\n"));
213
214 FinalRelease();
215 sInstance = NULL;
216
217 LogFlowFunc(("VirtualBox object deleted.\n"));
218 RTPrintf("Informational: VirtualBox object deleted.\n");
219 }
220
221 NS_IMETHOD_(nsrefcnt) Release()
222 {
223 /* we overload Release() to guarantee the VirtualBox destructor is
224 * always called on the main thread */
225
226 nsrefcnt count = VirtualBox::Release();
227
228 if (count == 1)
229 {
230 /* the last reference held by clients is being released
231 * (see GetInstance()) */
232
233 PRBool onMainThread = PR_TRUE;
234 nsCOMPtr<nsIEventQueue> q(gEventQ);
235 if (q)
236 {
237 q->IsOnCurrentThread(&onMainThread);
238 q = nsnull;
239 }
240
241 PRBool timerStarted = PR_FALSE;
242
243 /* sTimer is null if this call originates from FactoryDestructor()*/
244 if (sTimer != NULL)
245 {
246 LogFlowFunc(("Last VirtualBox instance was released.\n"));
247 LogFlowFunc(("Scheduling server shutdown in %u ms...\n",
248 gShutdownDelayMs));
249
250 /* make sure the previous timer (if any) is stopped;
251 * otherwise RTTimerStart() will definitely fail. */
252 RTTimerLRStop(sTimer);
253
254 int vrc = RTTimerLRStart(sTimer, gShutdownDelayMs * RT_NS_1MS_64);
255 AssertRC(vrc);
256 timerStarted = SUCCEEDED(vrc);
257 }
258 else
259 {
260 LogFlowFunc(("Last VirtualBox instance was released "
261 "on XPCOM shutdown.\n"));
262 Assert(onMainThread);
263 }
264
265 gAllowSigUsrQuit = PR_TRUE;
266
267 if (!timerStarted)
268 {
269 if (!onMainThread)
270 {
271 /* Failed to start the timer, post the shutdown event
272 * manually if not on the main thread already. */
273 ShutdownTimer(NULL, NULL, 0);
274 }
275 else
276 {
277 /* Here we come if:
278 *
279 * a) gEventQ is 0 which means either FactoryDestructor() is called
280 * or the IPC/DCONNECT shutdown sequence is initiated by the
281 * XPCOM shutdown routine (NS_ShutdownXPCOM()), which always
282 * happens on the main thread.
283 *
284 * b) gEventQ has reported we're on the main thread. This means
285 * that DestructEventHandler() has been called, but another
286 * client was faster and requested VirtualBox again.
287 *
288 * In either case, there is nothing to do.
289 *
290 * Note: case b) is actually no more valid since we don't
291 * call Release() from DestructEventHandler() in this case
292 * any more. Thus, we assert below.
293 */
294
295 Assert(!gEventQ);
296 }
297 }
298 }
299
300 return count;
301 }
302
303 class MaybeQuitEvent : public MyEvent
304 {
305 /* called on the main thread */
306 void *handler()
307 {
308 LogFlowFuncEnter();
309
310 Assert(RTCritSectIsInitialized(&sLock));
311
312 /* stop accepting GetInstance() requests on other threads during
313 * possible destruction */
314 RTCritSectEnter(&sLock);
315
316 nsrefcnt count = 0;
317
318 /* sInstance is NULL here if it was deleted immediately after
319 * creation due to initialization error. See GetInstance(). */
320 if (sInstance != NULL)
321 {
322 /* Release the guard reference added in GetInstance() */
323 count = sInstance->Release();
324 }
325
326 if (count == 0)
327 {
328 if (gAutoShutdown)
329 {
330 Assert(sInstance == NULL);
331 LogFlowFunc(("Terminating the server process...\n"));
332 /* make it leave the event loop */
333 gKeepRunning = PR_FALSE;
334 }
335 else
336 LogFlowFunc(("No automatic shutdown.\n"));
337 }
338 else
339 {
340 /* This condition is quite rare: a new client happened to
341 * connect after this event has been posted to the main queue
342 * but before it started to process it. */
343 LogFlowFunc(("Destruction is canceled (refcnt=%d).\n", count));
344 }
345
346 RTCritSectLeave(&sLock);
347
348 LogFlowFuncLeave();
349 return NULL;
350 }
351 };
352
353 static void ShutdownTimer(RTTIMERLR hTimerLR, void *pvUser, uint64_t /*iTick*/)
354 {
355 NOREF(hTimerLR);
356 NOREF(pvUser);
357
358 /* A "too late" event is theoretically possible if somebody
359 * manually ended the server after a destruction has been scheduled
360 * and this method was so lucky that it got a chance to run before
361 * the timer was killed. */
362 nsCOMPtr<nsIEventQueue> q(gEventQ);
363 AssertReturnVoid(q);
364
365 /* post a quit event to the main queue */
366 MaybeQuitEvent *ev = new MaybeQuitEvent();
367 nsresult rv = ev->postTo(q);
368 NOREF(rv);
369
370 /* A failure above means we've been already stopped (for example
371 * by Ctrl-C). FactoryDestructor() (NS_ShutdownXPCOM())
372 * will do the job. Nothing to do. */
373 }
374
375 static NS_IMETHODIMP FactoryConstructor()
376 {
377 LogFlowFunc(("\n"));
378
379 /* create a critsect to protect object construction */
380 if (RT_FAILURE(RTCritSectInit(&sLock)))
381 return NS_ERROR_OUT_OF_MEMORY;
382
383 int vrc = RTTimerLRCreateEx(&sTimer, 0, 0, ShutdownTimer, NULL);
384 if (RT_FAILURE(vrc))
385 {
386 LogFlowFunc(("Failed to create a timer! (vrc=%Rrc)\n", vrc));
387 return NS_ERROR_FAILURE;
388 }
389
390 return NS_OK;
391 }
392
393 static NS_IMETHODIMP FactoryDestructor()
394 {
395 LogFlowFunc(("\n"));
396
397 RTTimerLRDestroy(sTimer);
398 sTimer = NULL;
399
400 if (sInstance != NULL)
401 {
402 /* Either posting a destruction event failed for some reason (most
403 * likely, the quit event has been received before the last release),
404 * or the client has terminated abnormally w/o releasing its
405 * VirtualBox instance (so NS_ShutdownXPCOM() is doing a cleanup).
406 * Release the guard reference we added in GetInstance(). */
407 sInstance->Release();
408 }
409
410 /* Destroy lock after releasing the VirtualBox instance, otherwise
411 * there are races with cleanup. */
412 RTCritSectDelete(&sLock);
413
414 return NS_OK;
415 }
416
417 static nsresult GetInstance(VirtualBox **inst)
418 {
419 LogFlowFunc(("Getting VirtualBox object...\n"));
420
421 RTCritSectEnter(&sLock);
422
423 if (!gKeepRunning)
424 {
425 LogFlowFunc(("Process termination requested first. Refusing.\n"));
426
427 RTCritSectLeave(&sLock);
428
429 /* this rv is what CreateInstance() on the client side returns
430 * when the server process stops accepting events. Do the same
431 * here. The client wrapper should attempt to start a new process in
432 * response to a failure from us. */
433 return NS_ERROR_ABORT;
434 }
435
436 nsresult rv = NS_OK;
437
438 if (sInstance == NULL)
439 {
440 LogFlowFunc(("Creating new VirtualBox object...\n"));
441 sInstance = new VirtualBoxClassFactory();
442 if (sInstance != NULL)
443 {
444 /* make an extra AddRef to take the full control
445 * on the VirtualBox destruction (see FinalRelease()) */
446 sInstance->AddRef();
447
448 sInstance->AddRef(); /* protect FinalConstruct() */
449 rv = sInstance->FinalConstruct();
450 RTPrintf("Informational: VirtualBox object created (rc=%Rhrc).\n", rv);
451 if (NS_FAILED(rv))
452 {
453 /* On failure diring VirtualBox initialization, delete it
454 * immediately on the current thread by releasing all
455 * references in order to properly schedule the server
456 * shutdown. Since the object is fully deleted here, there
457 * is a chance to fix the error and request a new
458 * instantiation before the server terminates. However,
459 * the main reason to maintain the shutdown delay on
460 * failure is to let the front-end completely fetch error
461 * info from a server-side IVirtualBoxErrorInfo object. */
462 sInstance->Release();
463 sInstance->Release();
464 Assert(sInstance == NULL);
465 }
466 else
467 {
468 /* On success, make sure the previous timer is stopped to
469 * cancel a scheduled server termination (if any). */
470 gAllowSigUsrQuit = PR_FALSE;
471 RTTimerLRStop(sTimer);
472 }
473 }
474 else
475 {
476 rv = NS_ERROR_OUT_OF_MEMORY;
477 }
478 }
479 else
480 {
481 LogFlowFunc(("Using existing VirtualBox object...\n"));
482 nsrefcnt count = sInstance->AddRef();
483 Assert(count > 1);
484
485 if (count == 2)
486 {
487 LogFlowFunc(("Another client has requested a reference to VirtualBox, canceling destruction...\n"));
488
489 /* make sure the previous timer is stopped */
490 gAllowSigUsrQuit = PR_FALSE;
491 RTTimerLRStop(sTimer);
492 }
493 }
494
495 *inst = sInstance;
496
497 RTCritSectLeave(&sLock);
498
499 return rv;
500 }
501
502private:
503
504 /* Don't be confused that sInstance is of the *ClassFactory type. This is
505 * actually a singleton instance (*ClassFactory inherits the singleton
506 * class; we combined them just for "simplicity" and used "static" for
507 * factory methods. *ClassFactory here is necessary for a couple of extra
508 * methods. */
509
510 static VirtualBoxClassFactory *sInstance;
511 static RTCRITSECT sLock;
512
513 static RTTIMERLR sTimer;
514};
515
516VirtualBoxClassFactory *VirtualBoxClassFactory::sInstance = NULL;
517RTCRITSECT VirtualBoxClassFactory::sLock;
518
519RTTIMERLR VirtualBoxClassFactory::sTimer = NIL_RTTIMERLR;
520
521NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR_WITH_RC(VirtualBox, VirtualBoxClassFactory::GetInstance)
522
523////////////////////////////////////////////////////////////////////////////////
524
525typedef NSFactoryDestructorProcPtr NSFactoryConstructorProcPtr;
526
527/**
528 * Enhanced module component information structure.
529 *
530 * nsModuleComponentInfo lacks the factory construction callback, here we add
531 * it. This callback is called straight after a nsGenericFactory instance is
532 * successfully created in RegisterSelfComponents.
533 */
534struct nsModuleComponentInfoPlusFactoryConstructor
535{
536 /** standard module component information */
537 const nsModuleComponentInfo *mpModuleComponentInfo;
538 /** (optional) Factory Construction Callback */
539 NSFactoryConstructorProcPtr mFactoryConstructor;
540};
541
542/////////////////////////////////////////////////////////////////////////////
543
544/**
545 * Helper function to register self components upon start-up
546 * of the out-of-proc server.
547 */
548static nsresult
549RegisterSelfComponents(nsIComponentRegistrar *registrar,
550 const nsModuleComponentInfoPlusFactoryConstructor *aComponents,
551 PRUint32 count)
552{
553 nsresult rc = NS_OK;
554 const nsModuleComponentInfoPlusFactoryConstructor *info = aComponents;
555 for (PRUint32 i = 0; i < count && NS_SUCCEEDED(rc); i++, info++)
556 {
557 /* skip components w/o a constructor */
558 if (!info->mpModuleComponentInfo->mConstructor)
559 continue;
560 /* create a new generic factory for a component and register it */
561 nsIGenericFactory *factory;
562 rc = NS_NewGenericFactory(&factory, info->mpModuleComponentInfo);
563 if (NS_SUCCEEDED(rc) && info->mFactoryConstructor)
564 {
565 rc = info->mFactoryConstructor();
566 if (NS_FAILED(rc))
567 NS_RELEASE(factory);
568 }
569 if (NS_SUCCEEDED(rc))
570 {
571 rc = registrar->RegisterFactory(info->mpModuleComponentInfo->mCID,
572 info->mpModuleComponentInfo->mDescription,
573 info->mpModuleComponentInfo->mContractID,
574 factory);
575 NS_RELEASE(factory);
576 }
577 }
578 return rc;
579}
580
581/////////////////////////////////////////////////////////////////////////////
582
583static ipcIService *gIpcServ = nsnull;
584static const char *g_pszPidFile = NULL;
585
586class ForceQuitEvent : public MyEvent
587{
588 void *handler()
589 {
590 LogFlowFunc(("\n"));
591
592 gKeepRunning = PR_FALSE;
593
594 if (g_pszPidFile)
595 RTFileDelete(g_pszPidFile);
596
597 return NULL;
598 }
599};
600
601static void signal_handler(int sig)
602{
603 nsCOMPtr<nsIEventQueue> q(gEventQ);
604 if (q && gKeepRunning)
605 {
606 if (sig == SIGUSR1)
607 {
608 if (gAllowSigUsrQuit)
609 {
610 VirtualBoxClassFactory::MaybeQuitEvent *ev = new VirtualBoxClassFactory::MaybeQuitEvent();
611 ev->postTo(q);
612 }
613 /* else do nothing */
614 }
615 else
616 {
617 /* post a force quit event to the queue */
618 ForceQuitEvent *ev = new ForceQuitEvent();
619 ev->postTo(q);
620 }
621 }
622}
623
624static nsresult vboxsvcSpawnDaemonByReExec(const char *pszPath, bool fAutoShutdown, const char *pszPidFile)
625{
626 PRFileDesc *readable = nsnull, *writable = nsnull;
627 PRProcessAttr *attr = nsnull;
628 nsresult rv = NS_ERROR_FAILURE;
629 PRFileDesc *devNull;
630 unsigned args_index = 0;
631 // The ugly casts are necessary because the PR_CreateProcessDetached has
632 // a const array of writable strings as a parameter. It won't write. */
633 char * args[1 + 1 + 2 + 1];
634 args[args_index++] = (char *)pszPath;
635 if (fAutoShutdown)
636 args[args_index++] = (char *)"--auto-shutdown";
637 if (pszPidFile)
638 {
639 args[args_index++] = (char *)"--pidfile";
640 args[args_index++] = (char *)pszPidFile;
641 }
642 args[args_index++] = 0;
643
644 // Use a pipe to determine when the daemon process is in the position
645 // to actually process requests. The daemon will write "READY" to the pipe.
646 if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS)
647 goto end;
648 PR_SetFDInheritable(writable, PR_TRUE);
649
650 attr = PR_NewProcessAttr();
651 if (!attr)
652 goto end;
653
654 if (PR_ProcessAttrSetInheritableFD(attr, writable, VBOXSVC_STARTUP_PIPE_NAME) != PR_SUCCESS)
655 goto end;
656
657 devNull = PR_Open("/dev/null", PR_RDWR, 0);
658 if (!devNull)
659 goto end;
660
661 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull);
662 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull);
663 PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull);
664
665 if (PR_CreateProcessDetached(pszPath, (char * const *)args, nsnull, attr) != PR_SUCCESS)
666 goto end;
667
668 // Close /dev/null
669 PR_Close(devNull);
670 // Close the child end of the pipe to make it the only owner of the
671 // file descriptor, so that unexpected closing can be detected.
672 PR_Close(writable);
673 writable = nsnull;
674
675 char msg[10];
676 memset(msg, '\0', sizeof(msg));
677 if ( PR_Read(readable, msg, sizeof(msg)-1) != 5
678 || strcmp(msg, "READY"))
679 goto end;
680
681 rv = NS_OK;
682
683end:
684 if (readable)
685 PR_Close(readable);
686 if (writable)
687 PR_Close(writable);
688 if (attr)
689 PR_DestroyProcessAttr(attr);
690 return rv;
691}
692
693int main(int argc, char **argv)
694{
695 /*
696 * Initialize the VBox runtime without loading
697 * the support driver
698 */
699 int vrc = RTR3InitExe(argc, &argv, 0);
700 if (RT_FAILURE(vrc))
701 return RTMsgInitFailure(vrc);
702
703 static const RTGETOPTDEF s_aOptions[] =
704 {
705 { "--automate", 'a', RTGETOPT_REQ_NOTHING },
706 { "--auto-shutdown", 'A', RTGETOPT_REQ_NOTHING },
707 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
708 { "--shutdown-delay", 'D', RTGETOPT_REQ_UINT32 },
709 { "--pidfile", 'p', RTGETOPT_REQ_STRING },
710 { "--logfile", 'F', RTGETOPT_REQ_STRING },
711 { "--logrotate", 'R', RTGETOPT_REQ_UINT32 },
712 { "--logsize", 'S', RTGETOPT_REQ_UINT64 },
713 { "--loginterval", 'I', RTGETOPT_REQ_UINT32 }
714 };
715
716 const char *pszLogFile = NULL;
717 uint32_t cHistory = 10; // enable log rotation, 10 files
718 uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
719 uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
720 bool fDaemonize = false;
721 PRFileDesc *daemon_pipe_wr = nsnull;
722
723 RTGETOPTSTATE GetOptState;
724 vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
725 AssertRC(vrc);
726
727 RTGETOPTUNION ValueUnion;
728 while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
729 {
730 switch (vrc)
731 {
732 case 'a':
733 /* --automate mode means we are started by XPCOM on
734 * demand. Daemonize ourselves and activate
735 * auto-shutdown. */
736 gAutoShutdown = true;
737 fDaemonize = true;
738 break;
739
740 case 'A':
741 /* --auto-shutdown mode means we're already daemonized. */
742 gAutoShutdown = true;
743 break;
744
745 case 'd':
746 fDaemonize = true;
747 break;
748
749 case 'D':
750 gShutdownDelayMs = ValueUnion.u32;
751 break;
752
753 case 'p':
754 g_pszPidFile = ValueUnion.psz;
755 break;
756
757 case 'F':
758 pszLogFile = ValueUnion.psz;
759 break;
760
761 case 'R':
762 cHistory = ValueUnion.u32;
763 break;
764
765 case 'S':
766 uHistoryFileSize = ValueUnion.u64;
767 break;
768
769 case 'I':
770 uHistoryFileTime = ValueUnion.u32;
771 break;
772
773 case 'h':
774 RTPrintf("no help\n");
775 return 1;
776
777 case 'V':
778 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
779 return 0;
780
781 default:
782 return RTGetOptPrintError(vrc, &ValueUnion);
783 }
784 }
785
786 if (fDaemonize)
787 {
788 vboxsvcSpawnDaemonByReExec(argv[0], gAutoShutdown, g_pszPidFile);
789 exit(126);
790 }
791
792 nsresult rc;
793
794 /** @todo Merge this code with svcmain.cpp (use Logging.cpp?). */
795 char szLogFile[RTPATH_MAX];
796 if (!pszLogFile)
797 {
798 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
799 if (RT_SUCCESS(vrc))
800 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
801 }
802 else
803 {
804 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile))
805 vrc = VERR_NO_MEMORY;
806 }
807 if (RT_FAILURE(vrc))
808 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc);
809
810 char szError[RTPATH_MAX + 128];
811 vrc = com::VBoxLogRelCreate("XPCOM Server", szLogFile,
812 RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
813 VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
814 RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
815 cHistory, uHistoryFileTime, uHistoryFileSize,
816 szError, sizeof(szError));
817 if (RT_FAILURE(vrc))
818 return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", szError, vrc);
819
820 daemon_pipe_wr = PR_GetInheritedFD(VBOXSVC_STARTUP_PIPE_NAME);
821 RTEnvUnset("NSPR_INHERIT_FDS");
822
823 const nsModuleComponentInfo VirtualBoxInfo = {
824 "VirtualBox component",
825 NS_VIRTUALBOX_CID,
826 NS_VIRTUALBOX_CONTRACTID,
827 VirtualBoxConstructor, // constructor function
828 NULL, // registration function
829 NULL, // deregistration function
830 VirtualBoxClassFactory::FactoryDestructor, // factory destructor function
831 NS_CI_INTERFACE_GETTER_NAME(VirtualBoxWrap),
832 NULL, // language helper
833 &NS_CLASSINFO_NAME(VirtualBoxWrap),
834 0 // flags
835 };
836
837 const nsModuleComponentInfoPlusFactoryConstructor components[] = {
838 {
839 &VirtualBoxInfo,
840 VirtualBoxClassFactory::FactoryConstructor // factory constructor function
841 }
842 };
843
844 do
845 {
846 rc = com::Initialize();
847 if (NS_FAILED(rc))
848 {
849 RTMsgError("Failed to initialize XPCOM! (rc=%Rhrc)\n", rc);
850 break;
851 }
852
853 nsCOMPtr<nsIComponentRegistrar> registrar;
854 rc = NS_GetComponentRegistrar(getter_AddRefs(registrar));
855 if (NS_FAILED(rc))
856 {
857 RTMsgError("Failed to get component registrar! (rc=%Rhrc)", rc);
858 break;
859 }
860
861 registrar->AutoRegister(nsnull);
862 rc = RegisterSelfComponents(registrar, components,
863 NS_ARRAY_LENGTH(components));
864 if (NS_FAILED(rc))
865 {
866 RTMsgError("Failed to register server components! (rc=%Rhrc)", rc);
867 break;
868 }
869
870 /* get the main thread's event queue (afaik, the dconnect service always
871 * gets created upon XPCOM startup, so it will use the main (this)
872 * thread's event queue to receive IPC events) */
873 rc = NS_GetMainEventQ(getter_AddRefs(gEventQ));
874 if (NS_FAILED(rc))
875 {
876 RTMsgError("Failed to get the main event queue! (rc=%Rhrc)", rc);
877 break;
878 }
879
880 nsCOMPtr<ipcIService> ipcServ(do_GetService(IPC_SERVICE_CONTRACTID, &rc));
881 if (NS_FAILED(rc))
882 {
883 RTMsgError("Failed to get IPC service! (rc=%Rhrc)", rc);
884 break;
885 }
886
887 NS_ADDREF(gIpcServ = ipcServ);
888
889 LogFlowFunc(("Will use \"%s\" as server name.\n", VBOXSVC_IPC_NAME));
890
891 rc = gIpcServ->AddName(VBOXSVC_IPC_NAME);
892 if (NS_FAILED(rc))
893 {
894 LogFlowFunc(("Failed to register the server name (rc=%Rhrc (%08X))!\n"
895 "Is another server already running?\n", rc, rc));
896
897 RTMsgError("Failed to register the server name \"%s\" (rc=%Rhrc)!\n"
898 "Is another server already running?\n",
899 VBOXSVC_IPC_NAME, rc);
900 NS_RELEASE(gIpcServ);
901 break;
902 }
903
904 {
905 /* setup signal handling to convert some signals to a quit event */
906 struct sigaction sa;
907 sa.sa_handler = signal_handler;
908 sigemptyset(&sa.sa_mask);
909 sa.sa_flags = 0;
910 sigaction(SIGINT, &sa, NULL);
911 sigaction(SIGQUIT, &sa, NULL);
912 sigaction(SIGTERM, &sa, NULL);
913 sigaction(SIGTRAP, &sa, NULL);
914 sigaction(SIGUSR1, &sa, NULL);
915 }
916
917 {
918 char szBuf[80];
919 int iSize;
920
921 iSize = RTStrPrintf(szBuf, sizeof(szBuf),
922 VBOX_PRODUCT" XPCOM Server Version "
923 VBOX_VERSION_STRING);
924 for (int i = iSize; i > 0; i--)
925 putchar('*');
926 RTPrintf("\n%s\n", szBuf);
927 RTPrintf("(C) 2004-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
928 "All rights reserved.\n");
929#ifdef DEBUG
930 RTPrintf("Debug version.\n");
931#endif
932 }
933
934 if (daemon_pipe_wr != nsnull)
935 {
936 RTPrintf("\nStarting event loop....\n[send TERM signal to quit]\n");
937 /* now we're ready, signal the parent process */
938 PR_Write(daemon_pipe_wr, "READY", strlen("READY"));
939 /* close writing end of the pipe, its job is done */
940 PR_Close(daemon_pipe_wr);
941 }
942 else
943 RTPrintf("\nStarting event loop....\n[press Ctrl-C to quit]\n");
944
945 if (g_pszPidFile)
946 {
947 RTFILE hPidFile = NIL_RTFILE;
948 vrc = RTFileOpen(&hPidFile, g_pszPidFile, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
949 if (RT_SUCCESS(vrc))
950 {
951 char szBuf[32];
952 const char *lf = "\n";
953 RTStrFormatNumber(szBuf, getpid(), 10, 0, 0, 0);
954 RTFileWrite(hPidFile, szBuf, strlen(szBuf), NULL);
955 RTFileWrite(hPidFile, lf, strlen(lf), NULL);
956 RTFileClose(hPidFile);
957 }
958 }
959
960 // Increase the file table size to 10240 or as high as possible.
961 struct rlimit lim;
962 if (getrlimit(RLIMIT_NOFILE, &lim) == 0)
963 {
964 if ( lim.rlim_cur < 10240
965 && lim.rlim_cur < lim.rlim_max)
966 {
967 lim.rlim_cur = RT_MIN(lim.rlim_max, 10240);
968 if (setrlimit(RLIMIT_NOFILE, &lim) == -1)
969 RTPrintf("WARNING: failed to increase file descriptor limit. (%d)\n", errno);
970 }
971 }
972 else
973 RTPrintf("WARNING: failed to obtain per-process file-descriptor limit (%d).\n", errno);
974
975 PLEvent *ev;
976 while (gKeepRunning)
977 {
978 gEventQ->WaitForEvent(&ev);
979 gEventQ->HandleEvent(ev);
980 }
981
982 /* stop accepting new events. Clients that happen to resolve our
983 * name and issue a CreateInstance() request after this point will
984 * get NS_ERROR_ABORT once we handle the remaining messages. As a
985 * result, they should try to start a new server process. */
986 gEventQ->StopAcceptingEvents();
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 /* process any remaining events. These events may include
993 * CreateInstance() requests received right before we called
994 * StopAcceptingEvents() above, and those will fail. */
995 gEventQ->ProcessPendingEvents();
996
997 RTPrintf("Terminated event loop.\n");
998 }
999 while (0); // this scopes the nsCOMPtrs
1000
1001 NS_IF_RELEASE(gIpcServ);
1002 gEventQ = nsnull;
1003
1004 /* no nsCOMPtrs are allowed to be alive when you call com::Shutdown(). */
1005
1006 LogFlowFunc(("Calling com::Shutdown()...\n"));
1007 rc = com::Shutdown();
1008 LogFlowFunc(("Finished com::Shutdown() (rc=%Rhrc)\n", rc));
1009
1010 if (NS_FAILED(rc))
1011 RTMsgError("Failed to shutdown XPCOM! (rc=%Rhrc)", rc);
1012
1013 RTPrintf("XPCOM server has shutdown.\n");
1014
1015 if (g_pszPidFile)
1016 RTFileDelete(g_pszPidFile);
1017
1018 return 0;
1019}
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