VirtualBox

Changeset 98474 in vbox for trunk


Ignore:
Timestamp:
Feb 3, 2023 7:20:53 PM (2 years ago)
Author:
vboxsync
Message:

Additions: X11: Add possibility restart VBoxClient processes during Guest Additions update, bugref:10359.

This commit makes VBoxClient processes to temporary release reference to vboxguest kernel module and then
restart itself. So module can be reloaded and newly started VBoxClient instance will utilize updated module.

When VBoxClient starts in demonized mode, it forks a child process which represents actual service. Parent
process continues to run and its main function is to restart child when it crashes or terminates with exit
status != 0. This commit makes child process to catch SIGUSR1 and terminate with exit
status VBGLR3EXITCODERELOAD (currently equal to 2). Parent process will detect this and in turn will release
its reference to vboxguest kernel module, allowing to reload it. The parent process will then wait for SIGUSR1
itself. Once received, it will restart itself (loading new, updated VBoxClient process image). This is a part
of the procedure to install and reload/restart Guest Additions kernel modules and user services without
requiring guest reboot.

Location:
trunk/src/VBox/Additions/x11/VBoxClient
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/x11/VBoxClient/VBoxClient.h

    r98103 r98474  
    7777    /** @todo Should this also have a component relative to the X server number?
    7878     */
    79     const char *pszPidFilePath;
     79    const char *pszPidFilePathTemplate;
    8080    /** The usage options stuff for the --help screen. */
    8181    const char *pszUsage;
  • trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp

    r98103 r98474  
    429429    "shcl",                      /* szName */
    430430    "Shared Clipboard",          /* pszDescription */
    431     ".vboxclient-clipboard.pid", /* pszPidFilePath */
     431    ".vboxclient-clipboard",     /* pszPidFilePathTemplate */
    432432    NULL,                        /* pszUsage */
    433433    NULL,                        /* pszOptions */
  • trunk/src/VBox/Additions/x11/VBoxClient/display-svga-session.cpp

    r98103 r98474  
    4646 *
    4747 * Multiple instances of this daemon are allowed to run in parallel
    48  * with the following limitations (see also vbclSVGASessionPidFileLock()).
     48 * with the following limitations.
    4949 * A single user cannot run multiple daemon instances per single TTY device,
    5050 * however, multiple instances are allowed for the user on different
     
    6767#include <iprt/linux/sysfs.h>
    6868
    69 /** Lock file handle. */
    70 static RTFILE g_hPidFile;
    71 /** Full path to PID lock file. */
    72 static char g_szPidFilePath[RTPATH_MAX];
    7369
    7470/** Handle to IPC client connection. */
     
    117113
    118114/**
    119  * Prevent multiple instances of the service from start.
    120  *
    121  * @returns IPRT status code.
    122  */
    123 static int vbclSVGASessionPidFileLock(void)
    124 {
    125     int rc;
    126 
    127     /* Allow parallel running instances of the service for processes
    128      * which are running in separate X11/Wayland sessions. Compose
    129      * custom PID file name based on currently active TTY device. */
    130 
    131     char *pszPidFileName = RTStrAlloc(RTPATH_MAX);
    132     if (pszPidFileName)
    133     {
    134         rc = RTPathUserHome(g_szPidFilePath, sizeof(g_szPidFilePath));
    135         if (RT_SUCCESS(rc))
    136         {
    137             char pszActiveTTY[128];
    138             size_t cchRead;
    139 
    140             RT_ZERO(pszActiveTTY);
    141 
    142             RTStrAAppend(&pszPidFileName, ".vboxclient-vmsvga-session");
    143 
    144             rc = RTLinuxSysFsReadStrFile(pszActiveTTY, sizeof(pszActiveTTY) - 1 /* reserve last byte for string termination */,
    145                                          &cchRead, "class/tty/tty0/active");
    146             if (RT_SUCCESS(rc))
    147             {
    148                 RTStrAAppend(&pszPidFileName, "-");
    149                 RTStrAAppend(&pszPidFileName, pszActiveTTY);
    150             }
    151             else
    152                 VBClLogInfo("cannot detect currently active tty device, "
    153                             "multiple service instances for a single user will not be allowed, rc=%Rrc", rc);
    154 
    155             RTStrAAppend(&pszPidFileName, ".pid");
    156 
    157             RTPathAppend(g_szPidFilePath, sizeof(g_szPidFilePath), pszPidFileName);
    158 
    159             VBClLogVerbose(1, "lock file path: %s\n", g_szPidFilePath);
    160             rc = VbglR3PidFile(g_szPidFilePath, &g_hPidFile);
    161         }
    162         else
    163             VBClLogError("unable to get user home directory, rc=%Rrc\n", rc);
    164 
    165         RTStrFree(pszPidFileName);
    166     }
    167     else
    168         rc = VERR_NO_MEMORY;
    169 
    170     return rc;
    171 }
    172 
    173 /**
    174  * Release lock file.
    175  */
    176 static void vbclSVGASessionPidFileRelease(void)
    177 {
    178     VbglR3ClosePidFile(g_szPidFilePath, g_hPidFile);
    179 }
    180 
    181 /**
    182115 * @interface_method_impl{VBCLSERVICE,pfnInit}
    183116 */
     
    192125
    193126    VBClLogSetLogPrefix(pszLogPrefix);
    194 
    195     rc = vbclSVGASessionPidFileLock();
    196     if (RT_FAILURE(rc))
    197     {
    198         VBClLogVerbose(1, "cannot acquire pid lock, rc=%Rrc\n", rc);
    199         return rc;
    200     }
    201127
    202128    rc = RTCritSectInit(&g_hClientCritSect);
     
    517443    }
    518444
    519     vbclSVGASessionPidFileRelease();
    520 
    521445    return VINF_SUCCESS;
    522446}
     
    526450    "vmsvga-session",                   /* szName */
    527451    "VMSVGA display assistant",         /* pszDescription */
    528     NULL,                               /* pszPidFilePath (no pid file lock) */
     452    ".vboxclient-vmsvga-session",       /* pszPidFilePathTemplate */
    529453    NULL,                               /* pszUsage */
    530454    NULL,                               /* pszOptions */
  • trunk/src/VBox/Additions/x11/VBoxClient/display-svga-x11.cpp

    r98103 r98474  
    13911391    "dp-svga-x11",                      /* szName */
    13921392    "SVGA X11 display",                 /* pszDescription */
    1393     ".vboxclient-display-svga-x11.pid", /* pszPidFilePath */
     1393    ".vboxclient-display-svga-x11",     /* pszPidFilePathTemplate */
    13941394    NULL,                               /* pszUsage */
    13951395    NULL,                               /* pszOptions */
  • trunk/src/VBox/Additions/x11/VBoxClient/display.cpp

    r98103 r98474  
    294294    "dp-legacy-x11",                    /* szName */
    295295    "Legacy display assistant",         /* pszDescription */
    296     ".vboxclient-display.pid",          /* pszPidFilePath (no pid file lock) */
     296    ".vboxclient-display",              /* pszPidFilePathTemplate */
    297297    NULL,                               /* pszUsage */
    298298    NULL,                               /* pszOptions */
  • trunk/src/VBox/Additions/x11/VBoxClient/draganddrop.cpp

    r98103 r98474  
    38663866    "dnd",                         /* szName */
    38673867    "Drag'n'Drop",                 /* pszDescription */
    3868     ".vboxclient-draganddrop.pid", /* pszPidFilePath */
     3868    ".vboxclient-draganddrop",     /* pszPidFilePathTemplate */
    38693869    NULL,                          /* pszUsage */
    38703870    NULL,                          /* pszOptions */
  • trunk/src/VBox/Additions/x11/VBoxClient/hostversion.cpp

    r98103 r98474  
    119119    "hostversion",                   /* szName */
    120120    "VirtualBox host version check", /* pszDescription */
    121     ".vboxclient-hostversion.pid",   /* pszPidFilePath */
     121    ".vboxclient-hostversion",       /* pszPidFilePathTemplate */
    122122    NULL,                            /* pszUsage */
    123123    NULL,                            /* pszOptions */
  • trunk/src/VBox/Additions/x11/VBoxClient/main.cpp

    r98103 r98474  
    4545#include <iprt/stream.h>
    4646#include <iprt/env.h>
     47#include <iprt/process.h>
     48#include <iprt/linux/sysfs.h>
    4749#include <VBox/VBoxGuestLib.h>
    4850#include <VBox/err.h>
     
    107109 * cleanup routine. */
    108110static RTFILE        g_hPidFile;
     111/** The name of pidfile for parent (control) process. */
     112static char          g_szControlPidFile[RTPATH_MAX] = "";
     113/** The file handle of parent process pidfile. */
     114static RTFILE        g_hControlPidFile;
     115
    109116/** Global critical section held during the clean-up routine (to prevent it
    110117 * being called on multiple threads at once) or things which may not happen
     
    118125/** Absolute path to log file, if any. */
    119126static char          g_szLogFile[RTPATH_MAX + 128] = "";
     127/** Set by the signal handler when SIGUSR1 received. */
     128static volatile bool g_fProcessReloadRequested = false;
    120129
    121130/**
     
    212221        VBClLogVerbose(2, "Received signal %d\n", iSignal);
    213222        g_fSignalHandlerCalled = true;
     223
     224        /* In our internal convention, when VBoxClient process receives SIGUSR1,
     225         * this is a trigger for restarting a process with exec() call. Usually
     226         * happens as a result of Guest Additions update in order to seamlessly
     227         * restart newly installed binaries. */
     228        if (iSignal == SIGUSR1)
     229            g_fProcessReloadRequested = true;
    214230
    215231        /* Leave critical section before stopping the service. */
     
    368384
    369385/**
     386 * Wait for SIGUSR1 and re-exec.
     387 */
     388static void vbclHandleUpdateStarted(char *const argv[])
     389{
     390    /* Context of parent process */
     391    sigset_t signalMask;
     392    int      iSignal;
     393    int      rc;
     394
     395    /* Release reference to guest driver. */
     396    VbglR3Term();
     397
     398    sigemptyset(&signalMask);
     399    sigaddset(&signalMask, SIGUSR1);
     400    rc = pthread_sigmask(SIG_BLOCK, &signalMask, NULL);
     401
     402    if (rc == 0)
     403    {
     404        LogRel(("%s: waiting for Guest Additions installation to be completed\n",
     405                g_Service.pDesc->pszDesc));
     406
     407        /* Wait for SIGUSR1. */
     408        rc = sigwait(&signalMask, &iSignal);
     409        if (rc == 0)
     410        {
     411            LogRel(("%s: Guest Additions installation to be completed, reloading service\n",
     412                    g_Service.pDesc->pszDesc));
     413
     414            /* Release pidfile, otherwise new VBoxClient instance won't be able to quire it. */
     415            VBClShutdown(false);
     416
     417            rc = RTProcCreate(argv[0], argv, RTENV_DEFAULT,
     418                              RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SEARCH_PATH, NULL);
     419            if (RT_SUCCESS(rc))
     420                LogRel(("%s: service restarted\n", g_Service.pDesc->pszDesc));
     421            else
     422                LogRel(("%s: cannot replace running image with %s (%s), automatic service reloading has failed\n",
     423                        g_Service.pDesc->pszDesc, argv[0], strerror(errno)));
     424        }
     425        else
     426            LogRel(("%s: cannot wait for signal (%s), automatic service reloading has failed\n",
     427                    g_Service.pDesc->pszDesc, strerror(errno)));
     428    }
     429    else
     430        LogRel(("%s: failed to setup signal handler, automatic service reloading has failed\n",
     431                g_Service.pDesc->pszDesc));
     432
     433    exit(RT_BOOL(rc != 0));
     434}
     435
     436/**
     437 * Compose pidfile name.
     438 *
     439 * @returns IPRT status code.
     440 * @param   szBuf           Buffer to store pidfile name into.
     441 * @param   cbBuf           Size of buffer.
     442 * @param   szTemplate      Null-terminated string which contains pidfile name.
     443 * @param   fParentProcess  Whether pidfile path should be composed for
     444 *                          parent (control) process or for a child (actual
     445 *                          service) process.
     446 */
     447static int vbclGetPidfileName(char *szBuf, size_t cbBuf, const char *szTemplate,
     448                              bool fParentProcess)
     449{
     450    int rc;
     451    char pszActiveTTY[128];
     452    size_t cchRead;
     453
     454    RT_ZERO(pszActiveTTY);
     455
     456    AssertPtrReturn(szBuf, VERR_INVALID_PARAMETER);
     457    AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
     458    AssertPtrReturn(szTemplate, VERR_INVALID_PARAMETER);
     459
     460    rc = RTPathUserHome(szBuf, cbBuf);
     461    if (RT_FAILURE(rc))
     462        VBClLogFatalError("%s: getting home directory failed: %Rrc\n",
     463                          g_Service.pDesc->pszDesc, rc);
     464
     465    if (RT_SUCCESS(rc))
     466        rc = RTPathAppend(szBuf, cbBuf, szTemplate);
     467
     468#ifdef RT_OS_LINUX
     469    if (RT_SUCCESS(rc))
     470        rc = RTLinuxSysFsReadStrFile(pszActiveTTY, sizeof(pszActiveTTY) - 1 /* reserve last byte for string termination */,
     471                                     &cchRead, "class/tty/tty0/active");
     472    if (RT_SUCCESS(rc))
     473    {
     474        RTStrCat(szBuf, cbBuf, "-");
     475        RTStrCat(szBuf, cbBuf, pszActiveTTY);
     476    }
     477    else
     478        VBClLogInfo("%s: cannot detect currently active tty device, "
     479                    "multiple service instances for a single user will not be allowed, rc=%Rrc",
     480                    g_Service.pDesc->pszDesc, rc);
     481#endif /* RT_OS_LINUX */
     482
     483    if (RT_SUCCESS(rc))
     484        RTStrCat(szBuf, cbBuf, fParentProcess ? "-control.pid" : "-service.pid");
     485
     486    if (RT_FAILURE(rc))
     487        VBClLogFatalError("%s: reating PID file path failed: %Rrc\n",
     488                          g_Service.pDesc->pszDesc, rc);
     489
     490    return rc;
     491}
     492
     493/**
    370494 * The main loop for the VBoxClient daemon.
    371495 */
     
    378502    if (RT_FAILURE(rc))
    379503        return RTMsgInitFailure(rc);
     504
     505    /* A flag which is returned to the parent process when Guest Additions update started. */
     506    bool fUpdateStarted = false;
    380507
    381508    /* This should never be called twice in one process - in fact one Display
     
    606733    if (RT_FAILURE(rc))
    607734        VBClLogFatalError("Initializing critical section failed: %Rrc\n", rc);
    608     if (g_Service.pDesc->pszPidFilePath)
    609     {
    610         rc = RTPathUserHome(g_szPidFile, sizeof(g_szPidFile));
     735    if (g_Service.pDesc->pszPidFilePathTemplate)
     736    {
     737        /* Get pidfile name for parent (control) process. */
     738        rc = vbclGetPidfileName(g_szControlPidFile, sizeof(g_szControlPidFile), g_Service.pDesc->pszPidFilePathTemplate, true);
    611739        if (RT_FAILURE(rc))
    612             VBClLogFatalError("Getting home directory failed: %Rrc\n", rc);
    613         rc = RTPathAppend(g_szPidFile, sizeof(g_szPidFile), g_Service.pDesc->pszPidFilePath);
     740            return RTEXITCODE_FAILURE;
     741
     742        /* Get pidfile name for service process. */
     743        rc = vbclGetPidfileName(g_szPidFile, sizeof(g_szPidFile), g_Service.pDesc->pszPidFilePathTemplate, false);
    614744        if (RT_FAILURE(rc))
    615             VBClLogFatalError("Creating PID file path failed: %Rrc\n", rc);
     745            return RTEXITCODE_FAILURE;
    616746    }
    617747
    618748    if (fDaemonise)
    619         rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &g_cRespawn);
     749    {
     750        rc = VbglR3DaemonizeEx(false /* fNoChDir */, false /* fNoClose */, fRespawn, &g_cRespawn,
     751                               true /* fReturnOnUpdate */, &fUpdateStarted, g_szControlPidFile, &g_hControlPidFile);
     752        /* This combination only works in context of parent process. */
     753        if (RT_SUCCESS(rc) && fUpdateStarted)
     754            vbclHandleUpdateStarted(argv);
     755    }
     756
    620757    if (RT_FAILURE(rc))
    621758        VBClLogFatalError("Daemonizing service failed: %Rrc\n", rc);
     
    625762        rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
    626763        if (rc == VERR_FILE_LOCK_VIOLATION)  /* Already running. */
     764        {
     765            VBClLogInfo("%s: service already running, exitting\n",
     766                        g_Service.pDesc->pszDesc);
    627767            return RTEXITCODE_SUCCESS;
     768        }
    628769        if (RT_FAILURE(rc))
    629             VBClLogFatalError("Creating PID file failed: %Rrc\n", rc);
     770        {
     771            VBClLogFatalError("Creating PID file %s failed: %Rrc\n", g_szPidFile, rc);
     772            return RTEXITCODE_FAILURE;
     773        }
    630774    }
    631775
     
    737881    /** @todo r=andy Should we return an appropriate exit code if the service failed to init?
    738882     *               Must be tested carefully with our init scripts first. */
    739     return RTEXITCODE_SUCCESS;
    740 }
    741 
     883    return g_fProcessReloadRequested ? VBGLR3EXITCODERELOAD : RTEXITCODE_SUCCESS;
     884}
     885
  • trunk/src/VBox/Additions/x11/VBoxClient/seamless.cpp

    r98103 r98474  
    349349    "seamless",                 /* szName */
    350350    "Seamless Mode Support",    /* pszDescription */
    351     ".vboxclient-seamless.pid", /* pszPidFilePath */
     351    ".vboxclient-seamless",     /* pszPidFilePathTemplate */
    352352    NULL,                       /* pszUsage */
    353353    NULL,                       /* pszOptions */
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