VirtualBox

Changeset 33112 in vbox for trunk/src/VBox/Main/xpcom


Ignore:
Timestamp:
Oct 13, 2010 5:34:05 PM (14 years ago)
Author:
vboxsync
Message:

Main/xpcom: Start VBoxSVC just like VBoxXPCOMIPCD directly as a detached process, avoids the extra re-exec, a useless log file for debug builds and simplifies the code. We can still go back to the old approach as VBoxSVC is still capable of daemonizing itself.

Location:
trunk/src/VBox/Main/xpcom
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/xpcom/server.cpp

    r33044 r33112  
    2828#include <nsGenericFactory.h>
    2929
     30#include "prio.h"
     31#include "prproces.h"
     32
    3033#include "xpcom/server.h"
    3134
     
    4346#include <iprt/path.h>
    4447#include <iprt/timer.h>
     48#include <iprt/env.h>
    4549
    4650#include <signal.h>     // for the signal handler
     
    756760}
    757761
     762static nsresult vboxsvcSpawnDaemonByReExec(const char *pszPath)
     763{
     764    PRFileDesc *readable = nsnull, *writable = nsnull;
     765    PRProcessAttr *attr = nsnull;
     766    nsresult rv = NS_ERROR_FAILURE;
     767    PRFileDesc *devNull;
     768    // The ugly casts are necessary because the PR_CreateProcessDetached has
     769    // a const array of writable strings as a parameter. It won't write. */
     770    char * const args[] = { (char *)pszPath, (char *)"--auto-shutdown", 0 };
     771
     772    // Use a pipe to determine when the daemon process is in the position
     773    // to actually process requests. The daemon will write "READY" to the pipe.
     774    if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS)
     775        goto end;
     776    PR_SetFDInheritable(writable, PR_TRUE);
     777
     778    attr = PR_NewProcessAttr();
     779    if (!attr)
     780        goto end;
     781
     782    if (PR_ProcessAttrSetInheritableFD(attr, writable, VBOXSVC_STARTUP_PIPE_NAME) != PR_SUCCESS)
     783        goto end;
     784
     785    devNull = PR_Open("/dev/null", PR_RDWR, 0);
     786    if (!devNull)
     787        goto end;
     788
     789    PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull);
     790    PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull);
     791    PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull);
     792
     793    if (PR_CreateProcessDetached(pszPath, args, nsnull, attr) != PR_SUCCESS)
     794        goto end;
     795
     796    // Close /dev/null
     797    PR_Close(devNull);
     798    // Close the child end of the pipe to make it the only owner of the
     799    // file descriptor, so that unexpected closing can be detected.
     800    PR_Close(writable);
     801    writable = nsnull;
     802
     803    char msg[10];
     804    memset(msg, '\0', sizeof(msg));
     805    if (   PR_Read(readable, msg, sizeof(msg)-1) != 5
     806        || strcmp(msg, "READY"))
     807        goto end;
     808
     809    rv = NS_OK;
     810
     811end:
     812    if (readable)
     813        PR_Close(readable);
     814    if (writable)
     815        PR_Close(writable);
     816    if (attr)
     817        PR_DestroyProcessAttr(attr);
     818    return rv;
     819}
     820
    758821int main(int argc, char **argv)
    759822{
     
    770833        { "--daemonize",      'd', RTGETOPT_REQ_NOTHING },
    771834        { "--pidfile",        'p', RTGETOPT_REQ_STRING  },
    772         { "--pipe",           'P', RTGETOPT_REQ_UINT32  },
    773835    };
    774836
    775837    bool            fDaemonize = false;
    776     int             daemon_pipe_wr = -1;
     838    PRFileDesc      *daemon_pipe_wr = nsnull;
    777839
    778840    RTGETOPTSTATE   GetOptState;
     
    814876            }
    815877
    816             /* This is just an internal hack for passing the pipe write fd
    817                along to the final child.  Internal use only. */
    818             case 'P':
    819             {
    820                 daemon_pipe_wr = ValueUnion.u32;
    821                 break;
    822             }
    823 
    824878            case 'h':
    825879            {
     
    839893    }
    840894
    841 #ifdef RT_OS_OS2 /** @todo There is almost no need to make a special case of OS/2 here. Just the execv call needs to be told to create a background process... */
    842 
    843     /* nothing to do here, the process is supposed to be already
    844      * started daemonized when it is necessary */
    845     NOREF(fDaemonize);
    846 
    847 #else // !RT_OS_OS2
    848 
    849895    if (fDaemonize)
    850896    {
    851         /* create a pipe for communication between child and parent */
    852         int daemon_pipe_fds[2] = {-1, -1};
    853         if (pipe(daemon_pipe_fds) < 0)
    854         {
    855             RTMsgError("pipe() failed (errno = %d)", errno);
    856             return 1;
    857         }
    858         daemon_pipe_wr = daemon_pipe_fds[1];
    859         int daemon_pipe_rd = daemon_pipe_fds[0];
    860 
    861         pid_t childpid = fork();
    862         if (childpid == -1)
    863         {
    864             RTMsgError("fork() failed (errno = %d)", errno);
    865             return 1;
    866         }
    867 
    868         if (childpid != 0)
    869         {
    870             /* we're the parent process */
    871             bool fSuccess = false;
    872 
    873             /* close the writing end of the pipe */
    874             close(daemon_pipe_wr);
    875 
    876             /* try to read a message from the pipe */
    877             char msg[10 + 1];
    878             RT_ZERO(msg); /* initialize so it's NULL terminated */
    879             if (read(daemon_pipe_rd, msg, sizeof(msg) - 1) > 0)
    880             {
    881                 if (strcmp(msg, "READY") == 0)
    882                     fSuccess = true;
    883                 else
    884                     RTMsgError("Unknown message from child process (%s)", msg);
    885             }
    886             else
    887                 RTMsgError("0 bytes read from child process");
    888 
    889             /* close the reading end of the pipe as well and exit */
    890             close(daemon_pipe_rd);
    891             return fSuccess ? 0 : 1;
    892         }
    893         /* we're the child process */
    894 
    895         /* Create a new SID for the child process */
    896         pid_t sid = setsid();
    897         if (sid < 0)
    898         {
    899             RTMsgError("setsid() failed (errno = %d)", errno);
    900             return 1;
    901         }
    902 
    903         /* Need to do another for to get rid of the session leader status.
    904          * Otherwise any accidentally opened tty will automatically become a
    905          * controlling tty for the daemon process. */
    906         childpid = fork();
    907         if (childpid == -1)
    908         {
    909             RTMsgError("second fork() failed (errno = %d)", errno);
    910             return 1;
    911         }
    912 
    913         if (childpid != 0)
    914         {
    915             /* we're the parent process, just a dummy so terminate now */
    916             exit(0);
    917         }
    918 
    919         /* Close all file handles except for the write end of the pipe. */
    920         int fdMax;
    921         struct rlimit lim;
    922         if (getrlimit(RLIMIT_NOFILE, &lim) == 0)
    923             fdMax = (int)RT_MIN(lim.rlim_cur, 65535); /* paranoia */
    924         else
    925             fdMax = 1024;
    926         for (int fd = 0; fd < fdMax; fd++)
    927             if (fd != daemon_pipe_wr)
    928                 close(fd);
    929 
    930         /* Make sure the pipe isn't any of the standard handles. */
    931         if (daemon_pipe_wr <= 2)
    932         {
    933             if (dup2(daemon_pipe_wr, 3) == 3)
    934             {
    935                 close(daemon_pipe_wr);
    936                 daemon_pipe_wr = 3;
    937             }
    938         }
    939 
    940         /* Redirect the standard handles to NULL by opening /dev/null three times. */
    941         open("/dev/null", O_RDWR, 0);
    942         open("/dev/null", O_RDWR, 0);
    943         open("/dev/null", O_RDWR, 0);
    944 
    945         /*
    946          * On leopard we're no longer allowed to use some of the core API's
    947          * after forking - this will cause us to hit an int3.
    948          * So, we'll have to execv VBoxSVC once again and hand it the pipe
    949          * and all other relevant options.
    950          *
    951          * On FreeBSD the fork approach doesn't work. The child fails
    952          * during initialization of XPCOM for some unknown reason and
    953          * exits making it impossible to autostart VBoxSVC when starting
    954          * a frontend (debugger and strace don't contain any useful info).
    955          */
    956         const char *apszArgs[7 + 2];
    957         unsigned i = 0;
    958         apszArgs[i++] = argv[0];
    959         apszArgs[i++] = "--pipe";
    960         char szPipeArg[32];
    961         RTStrPrintf(szPipeArg, sizeof(szPipeArg), "%d", daemon_pipe_wr);
    962         apszArgs[i++] = szPipeArg;
    963         if (g_pszPidFile)
    964         {
    965             apszArgs[i++] = "--pidfile";
    966             apszArgs[i++] = g_pszPidFile;
    967         }
    968         if (gAutoShutdown)
    969             apszArgs[i++] = "--auto-shutdown";
    970         apszArgs[i++] = NULL;   Assert(i <= RT_ELEMENTS(apszArgs));
    971         execv(apszArgs[0], (char * const *)apszArgs);
     897        vboxsvcSpawnDaemonByReExec(argv[0]);
    972898        exit(126);
    973899    }
    974900
    975 #endif // !RT_OS_OS2
    976 
    977901    nsresult    rc;
     902
     903    daemon_pipe_wr = PR_GetInheritedFD(VBOXSVC_STARTUP_PIPE_NAME);
     904    RTEnvUnset("NSPR_INHERIT_FDS");
    978905
    979906    do
     
    1067994        }
    1068995
    1069         if (daemon_pipe_wr >= 0)
     996        if (daemon_pipe_wr != nsnull)
    1070997        {
    1071998            RTPrintf("\nStarting event loop....\n[send TERM signal to quit]\n");
    1072999            /* now we're ready, signal the parent process */
    1073             write(daemon_pipe_wr, "READY", strlen("READY"));
     1000            PR_Write(daemon_pipe_wr, "READY", strlen("READY"));
    10741001            /* close writing end of the pipe, its job is done */
    1075             close(daemon_pipe_wr);
     1002            PR_Close(daemon_pipe_wr);
    10761003        }
    10771004        else
  • trunk/src/VBox/Main/xpcom/server.h

    r28800 r33112  
    2828#define VBOXSVC_IPC_NAME "VBoxSVC-" VBOX_VERSION_STRING
    2929
     30
     31/**
     32 * Tag for the file descriptor passing for the daemonizing control.
     33 */
     34#define VBOXSVC_STARTUP_PIPE_NAME "vboxsvc:startup-pipe"
     35
    3036#endif /* ____H_LINUX_SERVER */
  • trunk/src/VBox/Main/xpcom/server_module.cpp

    r30631 r33112  
    3434#include <ipcdclient.h>
    3535
     36#include "prio.h"
     37#include "prproces.h"
     38
    3639// official XPCOM headers don't define it yet
    3740#define IPC_DCONNECTSERVICE_CONTRACTID \
     
    9194NS_DECL_CLASSINFO (VirtualBox)
    9295NS_IMPL_CI_INTERFACE_GETTER1 (VirtualBox, IVirtualBox)
     96
     97static nsresult vboxsvcSpawnDaemon(void)
     98{
     99    PRFileDesc *readable = nsnull, *writable = nsnull;
     100    PRProcessAttr *attr = nsnull;
     101    nsresult rv = NS_ERROR_FAILURE;
     102    PRFileDesc *devNull;
     103    // The ugly casts are necessary because the PR_CreateProcessDetached has
     104    // a const array of writable strings as a parameter. It won't write. */
     105    char * const args[] = { (char *)VBoxSVCPath, (char *)"--auto-shutdown", 0 };
     106
     107    // Use a pipe to determine when the daemon process is in the position
     108    // to actually process requests. The daemon will write "READY" to the pipe.
     109    if (PR_CreatePipe(&readable, &writable) != PR_SUCCESS)
     110        goto end;
     111    PR_SetFDInheritable(writable, PR_TRUE);
     112
     113    attr = PR_NewProcessAttr();
     114    if (!attr)
     115        goto end;
     116
     117    if (PR_ProcessAttrSetInheritableFD(attr, writable, VBOXSVC_STARTUP_PIPE_NAME) != PR_SUCCESS)
     118        goto end;
     119
     120    devNull = PR_Open("/dev/null", PR_RDWR, 0);
     121    if (!devNull)
     122        goto end;
     123
     124    PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, devNull);
     125    PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, devNull);
     126    PR_ProcessAttrSetStdioRedirect(attr, PR_StandardError, devNull);
     127
     128    if (PR_CreateProcessDetached(VBoxSVCPath, args, nsnull, attr) != PR_SUCCESS)
     129        goto end;
     130
     131    // Close /dev/null
     132    PR_Close(devNull);
     133    // Close the child end of the pipe to make it the only owner of the
     134    // file descriptor, so that unexpected closing can be detected.
     135    PR_Close(writable);
     136    writable = nsnull;
     137
     138    char msg[10];
     139    memset(msg, '\0', sizeof(msg));
     140    if (   PR_Read(readable, msg, sizeof(msg)-1) != 5
     141        || strcmp(msg, "READY"))
     142        goto end;
     143
     144    rv = NS_OK;
     145
     146end:
     147    if (readable)
     148        PR_Close(readable);
     149    if (writable)
     150        PR_Close(writable);
     151    if (attr)
     152        PR_DestroyProcessAttr(attr);
     153    return rv;
     154}
     155
    93156
    94157/**
     
    181244                startedOnce = true;
    182245
    183 #ifdef RT_OS_OS2
    184                 char * const args[] = { VBoxSVCPath, "--automate", 0 };
    185                 /* use NSPR because we want the process to be detached right
    186                  * at startup (it isn't possible to detach it later on),
    187                  * RTProcCreate() isn't yet capable of doing that. */
    188                 PRStatus rv = PR_CreateProcessDetached (VBoxSVCPath,
    189                                                         args, NULL, NULL);
    190                 if (rv != PR_SUCCESS)
    191                 {
    192                     rc = NS_ERROR_FAILURE;
     246                rc = vboxsvcSpawnDaemon();
     247                if (NS_FAILED(rc))
    193248                    break;
    194                 }
    195 #else
    196                 const char *args[] = { VBoxSVCPath, "--automate", 0 };
    197                 RTPROCESS pid = NIL_RTPROCESS;
    198                 vrc = RTProcCreate (VBoxSVCPath, args, RTENV_DEFAULT, 0, &pid);
    199                 if (RT_FAILURE(vrc))
    200                 {
    201                     rc = NS_ERROR_FAILURE;
    202                     break;
    203                 }
    204 
    205                 /* need to wait for the pid to avoid zombie VBoxSVC.
    206                  * ignore failure since it just means we'll have a zombie
    207                  * VBoxSVC until we exit */
    208                 int vrc2 = RTProcWait(pid, RTPROCWAIT_FLAGS_BLOCK, NULL);
    209                 AssertRC(vrc2);
    210 #endif
    211249
    212250                /* wait for the server process to establish a connection */
Note: See TracChangeset for help on using the changeset viewer.

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