VirtualBox

Ignore:
Timestamp:
Mar 8, 2013 2:44:06 PM (12 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
84180
Message:

VBoxService/GuestCtrl: More code (revamp) for dedicated guest session handling. Untested, work in progress.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp

    r44935 r44963  
    5353 *  the maximum number of processes is unlimited. */
    5454static uint32_t             g_uControlProcsMaxKept = 256;
    55 #ifdef DEBUG
    56  static bool                g_fControlDumpStdErr   = false;
    57  static bool                g_fControlDumpStdOut   = false;
    58 #endif
    59 /** List of active guest control threads (VBOXSERVICECTRLTHREAD). */
    60 static RTLISTANCHOR         g_lstControlThreadsActive;
    61 /** List of inactive guest control threads (VBOXSERVICECTRLTHREAD). */
    62 static RTLISTANCHOR         g_lstControlThreadsInactive;
    63 /** Critical section protecting g_GuestControlExecThreads. */
    64 static RTCRITSECT           g_csControlThreads;
    65 /** List of guest control sessions (VBOXSERVICECTRLSESSION). */
    66 RTLISTANCHOR                g_lstControlSessions;
     55/** List of guest control session threads (VBOXSERVICECTRLSESSIONTHREAD).
     56 *  A guest session thread represents a forked guest session process
     57 *  of VBoxService.  */
     58RTLISTANCHOR                g_lstControlSessionThreads;
     59/** The local session object used for handling all session-related stuff.
     60 *  When using the legacy guest control protocol (< 2), this session runs
     61 *  under behalf of the VBoxService main process. On newer protocol versions
     62 *  each session is a forked version of VBoxService using the appropriate
     63 *  user credentials for opening a guest session. */
     64VBOXSERVICECTRLSESSION      g_Session;
    6765
    6866/*******************************************************************************
     
    7169static int  gstcntlHandleSessionOpen(PVBGLR3GUESTCTRLHOSTCTX pHostCtx);
    7270static int  gstcntlHandleSessionClose(PVBGLR3GUESTCTRLHOSTCTX pHostCtx);
    73 static int  gstcntlHandleProcExec(PVBGLR3GUESTCTRLHOSTCTX pHostCtx);
    74 static int  gstcntlHandleProcInput(PVBGLR3GUESTCTRLHOSTCTX pHostCtx, void *pvScratchBuf, size_t cbScratchBuf);
    75 static int  gstcntlHandleProcOutput(PVBGLR3GUESTCTRLHOSTCTX pHostCtx);
    76 static int  gstcntlHandleProcTerminate(PVBGLR3GUESTCTRLHOSTCTX pHostCtx);
    77 static int  gstcntlHandleProcWaitFor(PVBGLR3GUESTCTRLHOSTCTX pHostCtx);
    78 static int  gstcntlReapThreads(void);
    7971static void VBoxServiceControlShutdown(void);
    80 static int  vboxServiceControlProcessCloseAll(void);
    81 static int  gstcntlStartAllowed(bool *pbAllowed);
    82 
    83 #ifdef DEBUG
    84 static int gstcntlDumpToFile(const char *pszFileName, void *pvBuf, size_t cbBuf)
    85 {
    86     AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
    87     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    88 
    89     if (!cbBuf)
    90         return VINF_SUCCESS;
    91 
    92     char szFile[RTPATH_MAX];
    93 
    94     int rc = RTPathTemp(szFile, sizeof(szFile));
    95     if (RT_SUCCESS(rc))
    96         rc = RTPathAppend(szFile, sizeof(szFile), pszFileName);
    97 
    98     if (RT_SUCCESS(rc))
    99     {
    100         VBoxServiceVerbose(4, "Dumping %ld bytes to \"%s\"\n", cbBuf, szFile);
    101 
    102         RTFILE fh;
    103         rc = RTFileOpen(&fh, szFile, RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
    104         if (RT_SUCCESS(rc))
    105         {
    106             rc = RTFileWrite(fh, pvBuf, cbBuf, NULL /* pcbWritten */);
    107             RTFileClose(fh);
    108         }
    109     }
    110 
    111     return rc;
    112 }
    113 #endif
    11472
    11573
     
    159117                                  &g_uControlIntervalMS, 1, UINT32_MAX - 1);
    160118#ifdef DEBUG
     119    else if (!strcmp(argv[*pi], "--control-dump-stdout"))
     120    {
     121        g_Session.uFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
     122        rc = 0; /* Flag this command as parsed. */
     123    }
    161124    else if (!strcmp(argv[*pi], "--control-dump-stderr"))
    162125    {
    163         g_fControlDumpStdErr = true;
    164         rc = 0; /* Flag this command as parsed. */
    165     }
    166     else if (!strcmp(argv[*pi], "--control-dump-stdout"))
    167     {
    168         g_fControlDumpStdOut = true;
     126        g_Session.uFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
    169127        rc = 0; /* Flag this command as parsed. */
    170128    }
     
    197155
    198156        /* Init lists. */
    199         RTListInit(&g_lstControlThreadsActive);
    200         RTListInit(&g_lstControlThreadsInactive);
    201         RTListInit(&g_lstControlSessions);
    202 
    203         /* Init critical section for protecting the thread lists. */
    204         rc = RTCritSectInit(&g_csControlThreads);
    205         AssertRC(rc);
     157        RTListInit(&g_lstControlSessionThreads);
    206158    }
    207159    else
     
    275227                /* Close all opened guest sessions -- all context IDs, sessions etc.
    276228                 * are now invalid. */
    277                 rc2 = vboxServiceControlProcessCloseAll();
     229                rc2 = GstCntlSessionCloseAll(&g_Session);
    278230                AssertRC(rc2);
    279231            }
     
    293245                    break;
    294246
    295                 case HOST_EXEC_CMD:
    296                     rc = gstcntlHandleProcExec(&ctxHost);
    297                     break;
    298 
    299                 case HOST_EXEC_SET_INPUT:
    300                     rc = gstcntlHandleProcInput(&ctxHost,
    301                                                 pvScratchBuf, cbScratchBuf);
    302                     break;
    303 
    304                 case HOST_EXEC_GET_OUTPUT:
    305                     rc = gstcntlHandleProcOutput(&ctxHost);
    306                     break;
    307 
    308                 case HOST_EXEC_TERMINATE:
    309                     rc = gstcntlHandleProcTerminate(&ctxHost);
    310                     break;
    311 
    312                 case HOST_EXEC_WAIT_FOR:
    313                     rc = gstcntlHandleProcWaitFor(&ctxHost);
    314                     break;
    315 
    316247                default:
    317                     VBoxServiceVerbose(3, "Unsupported message from host! Msg=%u\n", uMsg);
    318                     /* Don't terminate here; just wait for the next message. */
     248                    rc = GstCntlSessionHandler(&g_Session, uMsg, &ctxHost,
     249                                               pvScratchBuf, cbScratchBuf, pfShutdown);
    319250                    break;
    320251            }
     
    339270
    340271    VBoxServiceVerbose(0, "Guest control worker returned with rc=%Rrc\n", rc);
    341     return rc;
    342 }
    343 
    344 
    345 /**
    346  * Handles starting processes on the guest.
    347  *
    348  * @returns IPRT status code.
    349  * @param   uClientID       The HGCM client session ID.
    350  * @param   cParms          The number of parameters the host is offering.
    351  */
    352 static int gstcntlHandleProcExec(PVBGLR3GUESTCTRLHOSTCTX pHostCtx)
    353 {
    354     AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
    355 
    356     int rc;
    357     bool fStartAllowed = false; /* Flag indicating whether starting a process is allowed or not. */
    358 
    359     if (   (pHostCtx->uProtocol < 2  && pHostCtx->uNumParms == 11)
    360         || (pHostCtx->uProtocol >= 2 && pHostCtx->uNumParms == 12)
    361        )
    362     {
    363         VBOXSERVICECTRLPROCSTARTUPINFO proc;
    364         RT_ZERO(proc);
    365 
    366         /* Initialize maximum environment block size -- needed as input
    367          * parameter to retrieve the stuff from the host. On output this then
    368          * will contain the actual block size. */
    369         proc.cbEnv = sizeof(proc.szEnv);
    370 
    371         rc = VbglR3GuestCtrlProcGetStart(pHostCtx,
    372                                          /* Command */
    373                                          proc.szCmd,      sizeof(proc.szCmd),
    374                                          /* Flags */
    375                                          &proc.uFlags,
    376                                          /* Arguments */
    377                                          proc.szArgs,     sizeof(proc.szArgs), &proc.uNumArgs,
    378                                          /* Environment */
    379                                          proc.szEnv, &proc.cbEnv, &proc.uNumEnvVars,
    380                                          /* Credentials; for hosts with VBox < 4.3. */
    381                                          proc.szUser,     sizeof(proc.szUser),
    382                                          proc.szPassword, sizeof(proc.szPassword),
    383                                          /* Timelimit */
    384                                          &proc.uTimeLimitMS,
    385                                          /* Process priority */
    386                                          &proc.uPriority,
    387                                          /* Process affinity */
    388                                          proc.uAffinity,  sizeof(proc.uAffinity), &proc.uNumAffinity);
    389         if (RT_SUCCESS(rc))
    390         {
    391             VBoxServiceVerbose(3, "Request to start process szCmd=%s, uFlags=0x%x, szArgs=%s, szEnv=%s, szUser=%s, szPassword=%s, uTimeout=%RU32\n",
    392                                proc.szCmd, proc.uFlags,
    393                                proc.uNumArgs ? proc.szArgs : "<None>",
    394                                proc.uNumEnvVars ? proc.szEnv : "<None>",
    395                                proc.szUser,
    396 #ifdef DEBUG
    397                                proc.szPassword,
    398 #else
    399                                "XXX", /* Never show passwords in release mode. */
    400 #endif
    401                                proc.uTimeLimitMS);
    402 
    403             rc = gstcntlReapThreads();
    404             if (RT_FAILURE(rc))
    405                 VBoxServiceError("Reaping stopped processes failed with rc=%Rrc\n", rc);
    406             /* Keep going. */
    407 
    408             rc = gstcntlStartAllowed(&fStartAllowed);
    409             if (RT_SUCCESS(rc))
    410             {
    411                 if (fStartAllowed)
    412                 {
    413                     rc = GstCntlProcessStart(pHostCtx->uContextID, &proc);
    414                 }
    415                 else
    416                     rc = VERR_MAX_PROCS_REACHED; /* Maximum number of processes reached. */
    417             }
    418         }
    419     }
    420     else
    421         rc = VERR_NOT_SUPPORTED; /* Unsupported number of parameters. */
    422 
    423     /* In case of an error we need to notify the host to not wait forever for our response. */
    424     if (RT_FAILURE(rc))
    425     {
    426         VBoxServiceError("Starting process failed with rc=%Rrc\n", rc);
    427 
    428         /*
    429          * Note: The context ID can be 0 because we mabye weren't able to fetch the command
    430          *       from the host. The host in case has to deal with that!
    431          */
    432         int rc2 = VbglR3GuestCtrlProcCbStatus(pHostCtx->uClientID, pHostCtx->uContextID,
    433                                               0 /* PID, invalid */,
    434                                               PROC_STS_ERROR, rc,
    435                                               NULL /* pvData */, 0 /* cbData */);
    436         if (RT_FAILURE(rc2))
    437         {
    438             VBoxServiceError("Error sending start process status to host, rc=%Rrc\n", rc2);
    439             if (RT_SUCCESS(rc))
    440                 rc = rc2;
    441         }
    442     }
    443 
    444     return rc;
    445 }
    446 
    447 
    448 static int gstcntlHandleProcTerminate(PVBGLR3GUESTCTRLHOSTCTX pHostCtx)
    449 {
    450     AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
    451 
    452     uint32_t uPID;
    453     int rc = VbglR3GuestCtrlProcGetTerminate(pHostCtx, &uPID);
    454     if (RT_SUCCESS(rc))
    455     {
    456         PVBOXSERVICECTRLREQUEST pRequest;
    457         rc = GstCntlProcessRequestAllocEx(&pRequest, VBOXSERVICECTRLREQUEST_PROC_TERM,
    458                                           NULL /* pvBuf */, 0 /* cbBuf */, pHostCtx->uContextID);
    459         if (RT_SUCCESS(rc))
    460         {
    461             rc = GstCntlProcessPerform(uPID, pRequest);
    462             GstCntlProcessRequestFree(pRequest);
    463         }
    464     }
    465 
    466     return rc;
    467 }
    468 
    469 
    470 static int gstcntlHandleProcWaitFor(PVBGLR3GUESTCTRLHOSTCTX pHostCtx)
    471 {
    472     AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
    473 
    474     uint32_t uPID;
    475     uint32_t uWaitFlags; uint32_t uTimeoutMS;
    476 
    477     int rc = VbglR3GuestCtrlProcGetWaitFor(pHostCtx, &uPID, &uWaitFlags, &uTimeoutMS);
    478     if (RT_SUCCESS(rc))
    479     {
    480         PVBOXSERVICECTRLREQUEST pRequest;
    481         VBOXSERVICECTRLREQDATA_WAIT_FOR reqData = { uWaitFlags, uTimeoutMS };
    482         rc = GstCntlProcessRequestAllocEx(&pRequest, VBOXSERVICECTRLREQUEST_WAIT_FOR,
    483                                           &reqData, sizeof(reqData), pHostCtx->uContextID);
    484         if (RT_SUCCESS(rc))
    485         {
    486             rc = GstCntlProcessPerform(uPID, pRequest);
    487             GstCntlProcessRequestFree(pRequest);
    488         }
    489     }
    490 
    491     return rc;
    492 }
    493 
    494 
    495 /**
    496  * Gets output from stdout/stderr of a specified guest process.
    497  *
    498  * @return  IPRT status code.
    499  * @param   uPID                    PID of process to retrieve the output from.
    500  * @param   uCID                    Context ID.
    501  * @param   uHandleId               Stream ID (stdout = 0, stderr = 2) to get the output from.
    502  * @param   cMsTimeout              Timeout (in ms) to wait for output becoming
    503  *                                  available.
    504  * @param   pvBuf                   Pointer to a pre-allocated buffer to store the output.
    505  * @param   cbBuf                   Size (in bytes) of the pre-allocated buffer.
    506  * @param   pcbRead                 Pointer to number of bytes read.  Optional.
    507  */
    508 int VBoxServiceControlExecGetOutput(uint32_t uPID, uint32_t uCID,
    509                                     uint32_t uHandleId, uint32_t cMsTimeout,
    510                                     void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
    511 {
    512     AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
    513     AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    514     AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER);
    515 
    516     int                         rc      = VINF_SUCCESS;
    517     VBOXSERVICECTRLREQUESTTYPE  reqType = VBOXSERVICECTRLREQUEST_UNKNOWN; /* (gcc maybe, well, wrong.) */
    518     switch (uHandleId)
    519     {
    520         case OUTPUT_HANDLE_ID_STDERR:
    521             reqType = VBOXSERVICECTRLREQUEST_PROC_STDERR;
    522             break;
    523 
    524         case OUTPUT_HANDLE_ID_STDOUT:
    525         case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED:
    526             reqType = VBOXSERVICECTRLREQUEST_PROC_STDOUT;
    527             break;
    528 
    529         default:
    530             rc = VERR_INVALID_PARAMETER;
    531             break;
    532     }
    533 
    534     if (RT_SUCCESS(rc))
    535     {
    536         PVBOXSERVICECTRLREQUEST pRequest;
    537         rc = GstCntlProcessRequestAllocEx(&pRequest, reqType, pvBuf, cbBuf, uCID);
    538         if (RT_SUCCESS(rc))
    539         {
    540             rc = GstCntlProcessPerform(uPID, pRequest);
    541             if (RT_SUCCESS(rc) && pcbRead)
    542                 *pcbRead = pRequest->cbData;
    543             GstCntlProcessRequestFree(pRequest);
    544         }
    545     }
    546 
    547     return rc;
    548 }
    549 
    550 
    551 /**
    552  * Sets the specified guest thread to a certain list.
    553  *
    554  * @return  IPRT status code.
    555  * @param   enmList                 List to move thread to.
    556  * @param   pThread                 Thread to set inactive.
    557  */
    558 int GstCntlListSet(VBOXSERVICECTRLTHREADLISTTYPE enmList,
    559                    PVBOXSERVICECTRLTHREAD pThread)
    560 {
    561     AssertReturn(enmList > VBOXSERVICECTRLTHREADLIST_UNKNOWN, VERR_INVALID_PARAMETER);
    562     AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    563 
    564     int rc = RTCritSectEnter(&g_csControlThreads);
    565     if (RT_SUCCESS(rc))
    566     {
    567         VBoxServiceVerbose(3, "Setting thread (PID %RU32) to list %d\n",
    568                            pThread->uPID, enmList);
    569 
    570         PRTLISTANCHOR pAnchor = NULL;
    571         switch (enmList)
    572         {
    573             case VBOXSERVICECTRLTHREADLIST_STOPPED:
    574                 pAnchor = &g_lstControlThreadsInactive;
    575                 break;
    576 
    577             case VBOXSERVICECTRLTHREADLIST_RUNNING:
    578                 pAnchor = &g_lstControlThreadsActive;
    579                 break;
    580 
    581             default:
    582                 AssertMsgFailed(("Unknown list type: %u", enmList));
    583                 break;
    584         }
    585 
    586         if (!pAnchor)
    587             rc = VERR_INVALID_PARAMETER;
    588 
    589         if (RT_SUCCESS(rc))
    590         {
    591             if (pThread->pAnchor != NULL)
    592             {
    593                 /* If thread was assigned to a list before,
    594                  * remove the thread from the old list first. */
    595                 /* rc = */ RTListNodeRemove(&pThread->Node);
    596             }
    597 
    598             /* Add thread to desired list. */
    599             /* rc = */ RTListAppend(pAnchor, &pThread->Node);
    600             pThread->pAnchor = pAnchor;
    601         }
    602 
    603         int rc2 = RTCritSectLeave(&g_csControlThreads);
    604         if (RT_SUCCESS(rc))
    605             rc = rc2;
    606     }
    607 
    608     return VINF_SUCCESS;
    609 }
    610 
    611 
    612 /**
    613  * Injects input to a specified running process.
    614  *
    615  * @return  IPRT status code.
    616  * @param   uPID                    PID of process to set the input for.
    617  * @param   fPendingClose           Flag indicating whether this is the last input block sent to the process.
    618  * @param   pvBuf                   Pointer to a buffer containing the actual input data.
    619  * @param   cbBuf                   Size (in bytes) of the input buffer data.
    620  * @param   pcbWritten              Pointer to number of bytes written to the process.  Optional.
    621  */
    622 int VBoxServiceControlSetInput(uint32_t uPID, uint32_t uCID,
    623                                bool fPendingClose,
    624                                void *pvBuf, uint32_t cbBuf,
    625                                uint32_t *pcbWritten)
    626 {
    627     /* pvBuf is optional. */
    628     /* cbBuf is optional. */
    629     /* pcbWritten is optional. */
    630 
    631     PVBOXSERVICECTRLREQUEST pRequest;
    632     int rc = GstCntlProcessRequestAllocEx(&pRequest,
    633                                           fPendingClose
    634                                           ? VBOXSERVICECTRLREQUEST_PROC_STDIN_EOF
    635                                           : VBOXSERVICECTRLREQUEST_PROC_STDIN,
    636                                           pvBuf, cbBuf, uCID);
    637     if (RT_SUCCESS(rc))
    638     {
    639         rc = GstCntlProcessPerform(uPID, pRequest);
    640         if (RT_SUCCESS(rc))
    641         {
    642             if (pcbWritten)
    643                 *pcbWritten = pRequest->cbData;
    644         }
    645 
    646         GstCntlProcessRequestFree(pRequest);
    647     }
    648 
    649     return rc;
    650 }
    651 
    652 
    653 /**
    654  * Handles input for a started process by copying the received data into its
    655  * stdin pipe.
    656  *
    657  * @returns IPRT status code.
    658  * @param   pvScratchBuf                The scratch buffer.
    659  * @param   cbScratchBuf                The scratch buffer size for retrieving the input data.
    660  */
    661 static int gstcntlHandleProcInput(PVBGLR3GUESTCTRLHOSTCTX pHostCtx,
    662                                   void *pvScratchBuf, size_t cbScratchBuf)
    663 {
    664     AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
    665     AssertPtrReturn(cbScratchBuf, VERR_INVALID_PARAMETER);
    666     AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER);
    667 
    668     uint32_t uPID;
    669     uint32_t uFlags;
    670     uint32_t cbSize;
    671 
    672     uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */
    673     uint32_t cbWritten = 0; /* Number of bytes written to the guest. */
    674 
    675     /*
    676      * Ask the host for the input data.
    677      */
    678     int rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &uFlags,
    679                                          pvScratchBuf, cbScratchBuf, &cbSize);
    680     if (RT_FAILURE(rc))
    681     {
    682         VBoxServiceError("[PID %RU32]: Failed to retrieve exec input command! Error: %Rrc\n",
    683                          uPID, rc);
    684     }
    685     else if (cbSize > cbScratchBuf)
    686     {
    687         VBoxServiceError("[PID %RU32]: Too much input received! cbSize=%u, cbScratchBuf=%u\n",
    688                          uPID, cbSize, cbScratchBuf);
    689         rc = VERR_INVALID_PARAMETER;
    690     }
    691     else
    692     {
    693         /*
    694          * Is this the last input block we need to deliver? Then let the pipe know ...
    695          */
    696         bool fPendingClose = false;
    697         if (uFlags & INPUT_FLAG_EOF)
    698         {
    699             fPendingClose = true;
    700             VBoxServiceVerbose(4, "[PID %RU32]: Got last input block of size %u ...\n",
    701                                uPID, cbSize);
    702         }
    703 
    704         rc = VBoxServiceControlSetInput(uPID, pHostCtx->uContextID, fPendingClose, pvScratchBuf,
    705                                         cbSize, &cbWritten);
    706         VBoxServiceVerbose(4, "[PID %RU32]: Written input, CID=%u, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
    707                            uPID, pHostCtx->uContextID, rc, uFlags, fPendingClose, cbSize, cbWritten);
    708         if (RT_SUCCESS(rc))
    709         {
    710             uStatus = INPUT_STS_WRITTEN;
    711             uFlags = 0; /* No flags at the moment. */
    712         }
    713         else
    714         {
    715             if (rc == VERR_BAD_PIPE)
    716                 uStatus = INPUT_STS_TERMINATED;
    717             else if (rc == VERR_BUFFER_OVERFLOW)
    718                 uStatus = INPUT_STS_OVERFLOW;
    719         }
    720     }
    721 
    722     /*
    723      * If there was an error and we did not set the host status
    724      * yet, then do it now.
    725      */
    726     if (   RT_FAILURE(rc)
    727         && uStatus == INPUT_STS_UNDEFINED)
    728     {
    729         uStatus = INPUT_STS_ERROR;
    730         uFlags = rc;
    731     }
    732     Assert(uStatus > INPUT_STS_UNDEFINED);
    733 
    734     VBoxServiceVerbose(3, "[PID %RU32]: Input processed, CID=%u, uStatus=%u, uFlags=0x%x, cbWritten=%u\n",
    735                        uPID, pHostCtx->uContextID, uStatus, uFlags, cbWritten);
    736 
    737     /* Note: Since the context ID is unique the request *has* to be completed here,
    738      *       regardless whether we got data or not! Otherwise the progress object
    739      *       on the host never will get completed! */
    740     rc = VbglR3GuestCtrlProcCbStatusInput(pHostCtx->uClientID, pHostCtx->uContextID, uPID,
    741                                           uStatus, uFlags, (uint32_t)cbWritten);
    742 
    743     if (RT_FAILURE(rc))
    744         VBoxServiceError("[PID %RU32]: Failed to report input status! Error: %Rrc\n",
    745                          uPID, rc);
    746     return rc;
    747 }
    748 
    749 
    750 /**
    751  * Handles the guest control output command.
    752  *
    753  * @return  IPRT status code.
    754  */
    755 static int gstcntlHandleProcOutput(PVBGLR3GUESTCTRLHOSTCTX pHostCtx)
    756 {
    757     AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
    758 
    759     uint32_t uPID;
    760     uint32_t uHandleID;
    761     uint32_t uFlags;
    762 
    763     int rc = VbglR3GuestCtrlProcGetOutput(pHostCtx, &uPID, &uHandleID, &uFlags);
    764     if (RT_SUCCESS(rc))
    765     {
    766         uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K);
    767         if (pBuf)
    768         {
    769             uint32_t cbRead = 0;
    770             rc = VBoxServiceControlExecGetOutput(uPID, pHostCtx->uContextID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
    771                                                  pBuf, _64K /* cbSize */, &cbRead);
    772             VBoxServiceVerbose(3, "[PID %RU32]: Got output, rc=%Rrc, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
    773                                uPID, rc, pHostCtx->uContextID, cbRead, uHandleID, uFlags);
    774 
    775 #ifdef DEBUG
    776             if (   g_fControlDumpStdErr
    777                 && uHandleID == OUTPUT_HANDLE_ID_STDERR)
    778             {
    779                 char szPID[RTPATH_MAX];
    780                 if (!RTStrPrintf(szPID, sizeof(szPID), "VBoxService_PID%u_StdOut.txt", uPID))
    781                     rc = VERR_BUFFER_UNDERFLOW;
    782                 if (RT_SUCCESS(rc))
    783                     rc = gstcntlDumpToFile(szPID, pBuf, cbRead);
    784             }
    785             else if (   g_fControlDumpStdOut
    786                      && (   uHandleID == OUTPUT_HANDLE_ID_STDOUT
    787                          || uHandleID == OUTPUT_HANDLE_ID_STDOUT_DEPRECATED))
    788             {
    789                 char szPID[RTPATH_MAX];
    790                 if (!RTStrPrintf(szPID, sizeof(szPID), "VBoxService_PID%u_StdOut.txt", uPID))
    791                     rc = VERR_BUFFER_UNDERFLOW;
    792                 if (RT_SUCCESS(rc))
    793                     rc = gstcntlDumpToFile(szPID, pBuf, cbRead);
    794                 AssertRC(rc);
    795             }
    796 #endif
    797             /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary
    798              *        data which the host needs to work with -- so just pass through all data unfiltered! */
    799 
    800             /* Note: Since the context ID is unique the request *has* to be completed here,
    801              *       regardless whether we got data or not! Otherwise the progress object
    802              *       on the host never will get completed! */
    803             int rc2 = VbglR3GuestCtrlProcCbOutput(pHostCtx->uClientID, pHostCtx->uContextID, uPID, uHandleID, uFlags,
    804                                                   pBuf, cbRead);
    805             if (RT_SUCCESS(rc))
    806                 rc = rc2;
    807             else if (rc == VERR_NOT_FOUND) /* It's not critical if guest process (PID) is not found. */
    808                 rc = VINF_SUCCESS;
    809 
    810             RTMemFree(pBuf);
    811         }
    812         else
    813             rc = VERR_NO_MEMORY;
    814     }
    815 
    816     if (RT_FAILURE(rc))
    817         VBoxServiceError("[PID %RU32]: Error handling output command! Error: %Rrc\n",
    818                          uPID, rc);
    819272    return rc;
    820273}
     
    840293                           pHostCtx->uClientID, pHostCtx->uProtocol);
    841294
    842         rc = GstCntlSessionOpen(&ssInfo, NULL /* Node */);
     295        rc = GstCntlSessionThreadOpen(&g_lstControlSessionThreads,
     296                                      &ssInfo, NULL /* Session */);
    843297    }
    844298
     
    869323        rc = VERR_NOT_FOUND;
    870324
    871         PVBOXSERVICECTRLSESSION pSession;
    872         RTListForEach(&g_lstControlSessions, pSession, VBOXSERVICECTRLSESSION, Node)
     325        PVBOXSERVICECTRLSESSIONTHREAD pSession;
     326        RTListForEach(&g_lstControlSessionThreads, pSession, VBOXSERVICECTRLSESSIONTHREAD, Node)
    873327        {
    874328            if (pSession->StartupInfo.uSessionID == uSessionID)
    875329            {
    876                 rc = GstCntlSessionClose(pSession, uFlags);
     330                rc = GstCntlSessionThreadClose(pSession, uFlags);
    877331                break;
    878332            }
     
    923377
    924378/**
    925  * Reaps all inactive guest process threads.
    926  *
    927  * @return  IPRT status code.
    928  */
    929 static int gstcntlReapThreads(void)
    930 {
    931     int rc = RTCritSectEnter(&g_csControlThreads);
    932     if (RT_SUCCESS(rc))
    933     {
    934         PVBOXSERVICECTRLTHREAD pThread =
    935             RTListGetFirst(&g_lstControlThreadsInactive, VBOXSERVICECTRLTHREAD, Node);
    936         while (pThread)
    937         {
    938             PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node);
    939             bool fLast = RTListNodeIsLast(&g_lstControlThreadsInactive, &pThread->Node);
    940             int rc2 = GstCntlProcessWait(pThread, 30 * 1000 /* 30 seconds max. */,
    941                                          NULL /* rc */);
    942             if (RT_SUCCESS(rc2))
    943             {
    944                 RTListNodeRemove(&pThread->Node);
    945 
    946                 rc2 = GstCntlProcessFree(pThread);
    947                 if (RT_FAILURE(rc2))
    948                 {
    949                     VBoxServiceError("Freeing guest process thread failed with rc=%Rrc\n", rc2);
    950                     if (RT_SUCCESS(rc)) /* Keep original failure. */
    951                         rc = rc2;
    952                 }
    953             }
    954             else
    955                 VBoxServiceError("Waiting on guest process thread failed with rc=%Rrc\n", rc2);
    956             /* Keep going. */
    957 
    958             if (fLast)
    959                 break;
    960 
    961             pThread = pNext;
    962         }
    963 
    964         int rc2 = RTCritSectLeave(&g_csControlThreads);
    965         if (RT_SUCCESS(rc))
    966             rc = rc2;
    967     }
    968 
    969     VBoxServiceVerbose(4, "Reaping threads returned with rc=%Rrc\n", rc);
    970     return rc;
    971 }
    972 
    973 
    974 static int vboxServiceControlProcessClose()
    975 {
    976     /** Note: This will be a guest tsession task later. */
    977 
    978     /* Signal all threads in the active list that we want to shutdown. */
    979     PVBOXSERVICECTRLTHREAD pThread;
    980     RTListForEach(&g_lstControlThreadsActive, pThread, VBOXSERVICECTRLTHREAD, Node)
    981         GstCntlProcessStop(pThread);
    982 
    983     /* Wait for all active threads to shutdown and destroy the active thread list. */
    984     pThread = RTListGetFirst(&g_lstControlThreadsActive, VBOXSERVICECTRLTHREAD, Node);
    985     while (pThread)
    986     {
    987         PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node);
    988         bool fLast = RTListNodeIsLast(&g_lstControlThreadsActive, &pThread->Node);
    989 
    990         int rc2 = GstCntlProcessWait(pThread,
    991                                      30 * 1000 /* Wait 30 seconds max. */,
    992                                      NULL /* rc */);
    993         if (RT_FAILURE(rc2))
    994         {
    995             VBoxServiceError("Guest process thread failed to stop; rc=%Rrc\n", rc2);
    996             /* Keep going. */
    997         }
    998 
    999         if (fLast)
    1000             break;
    1001 
    1002         pThread = pNext;
    1003     }
    1004 
    1005     int rc = gstcntlReapThreads();
    1006     if (RT_FAILURE(rc))
    1007         VBoxServiceError("Reaping inactive threads failed with rc=%Rrc\n", rc);
    1008 
    1009     AssertMsg(RTListIsEmpty(&g_lstControlThreadsActive),
    1010               ("Guest process active thread list still contains entries when it should not\n"));
    1011     AssertMsg(RTListIsEmpty(&g_lstControlThreadsInactive),
    1012               ("Guest process inactive thread list still contains entries when it should not\n"));
    1013 
    1014     return rc;
    1015 }
    1016 
    1017 
    1018 static int vboxServiceControlProcessCloseAll(void)
    1019 {
    1020     return vboxServiceControlProcessClose();
    1021 }
    1022 
    1023 
    1024 /**
    1025379 * Destroys all guest process threads which are still active.
    1026380 */
     
    1029383    VBoxServiceVerbose(2, "Shutting down ...\n");
    1030384
    1031     int rc2 = vboxServiceControlProcessCloseAll();
    1032     AssertRC(rc2);
    1033 
    1034     /* Destroy critical section. */
    1035     RTCritSectDelete(&g_csControlThreads);
     385    /* int GstCntlSessionDestroy(PVBOXSERVICECTRLSESSION pSession) */
    1036386
    1037387    VBoxServiceVerbose(2, "Shutting down complete\n");
     
    1056406        g_hControlEvent = NIL_RTSEMEVENTMULTI;
    1057407    }
    1058 }
    1059 
    1060 
    1061 /**
    1062  * Determines whether starting a new guest process according to the
    1063  * maximum number of concurrent guest processes defined is allowed or not.
    1064  *
    1065  * @return  IPRT status code.
    1066  * @param   pbAllowed           True if starting (another) guest process
    1067  *                              is allowed, false if not.
    1068  */
    1069 static int gstcntlStartAllowed(bool *pbAllowed)
    1070 {
    1071     AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
    1072 
    1073     int rc = RTCritSectEnter(&g_csControlThreads);
    1074     if (RT_SUCCESS(rc))
    1075     {
    1076         /*
    1077          * Check if we're respecting our memory policy by checking
    1078          * how many guest processes are started and served already.
    1079          */
    1080         bool fLimitReached = false;
    1081         if (g_uControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
    1082         {
    1083             uint32_t uProcsRunning = 0;
    1084             PVBOXSERVICECTRLTHREAD pThread;
    1085             RTListForEach(&g_lstControlThreadsActive, pThread, VBOXSERVICECTRLTHREAD, Node)
    1086                 uProcsRunning++;
    1087 
    1088             VBoxServiceVerbose(3, "Maximum served guest processes set to %u, running=%u\n",
    1089                                g_uControlProcsMaxKept, uProcsRunning);
    1090 
    1091             int32_t iProcsLeft = (g_uControlProcsMaxKept - uProcsRunning - 1);
    1092             if (iProcsLeft < 0)
    1093             {
    1094                 VBoxServiceVerbose(3, "Maximum running guest processes reached (%u)\n",
    1095                                    g_uControlProcsMaxKept);
    1096                 fLimitReached = true;
    1097             }
    1098         }
    1099 
    1100         *pbAllowed = !fLimitReached;
    1101 
    1102         int rc2 = RTCritSectLeave(&g_csControlThreads);
    1103         if (RT_SUCCESS(rc))
    1104             rc = rc2;
    1105     }
    1106 
    1107     return rc;
    1108 }
    1109 
    1110 
    1111 /**
    1112  * Finds a (formerly) started process given by its PID and locks it. Must be unlocked
    1113  * by the caller with VBoxServiceControlThreadUnlock().
    1114  *
    1115  * @return  PVBOXSERVICECTRLTHREAD      Process structure if found, otherwise NULL.
    1116  * @param   uPID                        PID to search for.
    1117  */
    1118 PVBOXSERVICECTRLTHREAD GstCntlLockThread(uint32_t uPID)
    1119 {
    1120     PVBOXSERVICECTRLTHREAD pThread = NULL;
    1121     int rc = RTCritSectEnter(&g_csControlThreads);
    1122     if (RT_SUCCESS(rc))
    1123     {
    1124         PVBOXSERVICECTRLTHREAD pThreadCur;
    1125         RTListForEach(&g_lstControlThreadsActive, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
    1126         {
    1127             if (pThreadCur->uPID == uPID)
    1128             {
    1129                 rc = RTCritSectEnter(&pThreadCur->CritSect);
    1130                 if (RT_SUCCESS(rc))
    1131                     pThread = pThreadCur;
    1132                 break;
    1133             }
    1134         }
    1135 
    1136         int rc2 = RTCritSectLeave(&g_csControlThreads);
    1137         if (RT_SUCCESS(rc))
    1138             rc = rc2;
    1139     }
    1140 
    1141     return pThread;
    1142 }
    1143 
    1144 
    1145 /**
    1146  * Unlocks a previously locked guest process thread.
    1147  *
    1148  * @param   pThread                 Thread to unlock.
    1149  */
    1150 void GstCntlUnlockThread(const PVBOXSERVICECTRLTHREAD pThread)
    1151 {
    1152     AssertPtr(pThread);
    1153 
    1154     int rc = RTCritSectLeave(&pThread->CritSect);
    1155     AssertRC(rc);
    1156 }
    1157 
    1158 
    1159 /**
    1160  * Assigns a valid PID to a guest control thread and also checks if there already was
    1161  * another (stale) guest process which was using that PID before and destroys it.
    1162  *
    1163  * @return  IPRT status code.
    1164  * @param   pThread        Thread to assign PID to.
    1165  * @param   uPID           PID to assign to the specified guest control execution thread.
    1166  */
    1167 int GstCntlAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID)
    1168 {
    1169     AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    1170     AssertReturn(uPID, VERR_INVALID_PARAMETER);
    1171 
    1172     int rc = RTCritSectEnter(&g_csControlThreads);
    1173     if (RT_SUCCESS(rc))
    1174     {
    1175         /* Search old threads using the desired PID and shut them down completely -- it's
    1176          * not used anymore. */
    1177         PVBOXSERVICECTRLTHREAD pThreadCur;
    1178         bool fTryAgain = false;
    1179         do
    1180         {
    1181             RTListForEach(&g_lstControlThreadsActive, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
    1182             {
    1183                 if (pThreadCur->uPID == uPID)
    1184                 {
    1185                     Assert(pThreadCur != pThread); /* can't happen */
    1186                     uint32_t uTriedPID = uPID;
    1187                     uPID += 391939;
    1188                     VBoxServiceVerbose(2, "PID %RU32 was used before, trying again with %u ...\n",
    1189                                        uTriedPID, uPID);
    1190                     fTryAgain = true;
    1191                     break;
    1192                 }
    1193             }
    1194         } while (fTryAgain);
    1195 
    1196         /* Assign PID to current thread. */
    1197         pThread->uPID = uPID;
    1198 
    1199         rc = RTCritSectLeave(&g_csControlThreads);
    1200         AssertRC(rc);
    1201     }
    1202 
    1203     return rc;
    1204408}
    1205409
Note: See TracChangeset for help on using the changeset viewer.

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