VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp@ 39843

Last change on this file since 39843 was 39843, checked in by vboxsync, 13 years ago

GuestCtrl: Request (IPC) changes, bugfixes, fixed handle leaks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.8 KB
Line 
1/* $Id: VBoxServiceControl.cpp 39843 2012-01-23 18:38:18Z vboxsync $ */
2/** @file
3 * VBoxServiceControl - Host-driven Guest Control.
4 */
5
6/*
7 * Copyright (C) 2011 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
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/asm.h>
23#include <iprt/assert.h>
24#include <iprt/getopt.h>
25#include <iprt/mem.h>
26#include <iprt/semaphore.h>
27#include <iprt/thread.h>
28#include <VBox/VBoxGuestLib.h>
29#include <VBox/HostServices/GuestControlSvc.h>
30#include "VBoxServiceInternal.h"
31#include "VBoxServiceUtils.h"
32
33using namespace guestControl;
34
35/*******************************************************************************
36* Global Variables *
37*******************************************************************************/
38/** The control interval (milliseconds). */
39static uint32_t g_cMsControlInterval = 0;
40/** The semaphore we're blocking our main control thread on. */
41static RTSEMEVENTMULTI g_hControlEvent = NIL_RTSEMEVENTMULTI;
42/** The guest control service client ID. */
43static uint32_t g_GuestControlSvcClientID = 0;
44/** How many started guest processes are kept into memory for supplying
45 * information to the host. Default is 25 processes. If 0 is specified,
46 * the maximum number of processes is unlimited. */
47static uint32_t g_GuestControlProcsMaxKept = 25;
48/** List of guest control threads (VBOXSERVICECTRLTHREAD). */
49static RTLISTANCHOR g_GuestControlThreads;
50/** Critical section protecting g_GuestControlExecThreads. */
51static RTCRITSECT g_GuestControlThreadsCritSect;
52
53
54/*******************************************************************************
55* Internal Functions *
56*******************************************************************************/
57/** @todo Shorten "VBoxServiceControl" to "gstsvcCntl". */
58static int VBoxServiceControlStartAllowed(bool *pbAllowed);
59static int VBoxServiceControlHandleCmdStartProc(uint32_t u32ClientId, uint32_t uNumParms);
60static int VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize);
61static int VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms);
62
63
64
65/** @copydoc VBOXSERVICE::pfnPreInit */
66static DECLCALLBACK(int) VBoxServiceControlPreInit(void)
67{
68#ifdef VBOX_WITH_GUEST_PROPS
69 /*
70 * Read the service options from the VM's guest properties.
71 * Note that these options can be overridden by the command line options later.
72 */
73 uint32_t uGuestPropSvcClientID;
74 int rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
75 if (RT_FAILURE(rc))
76 {
77 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
78 {
79 VBoxServiceVerbose(0, "Control: Guest property service is not available, skipping\n");
80 rc = VINF_SUCCESS;
81 }
82 else
83 VBoxServiceError("Control: Failed to connect to the guest property service! Error: %Rrc\n", rc);
84 }
85 else
86 {
87 rc = VBoxServiceReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--control-procs-max-kept",
88 &g_GuestControlProcsMaxKept, 0, UINT32_MAX - 1);
89
90 VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
91 }
92
93 if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
94 rc = VINF_SUCCESS;
95 return rc;
96#else
97 /* Nothing to do here yet. */
98 return VINF_SUCCESS;
99#endif
100}
101
102
103/** @copydoc VBOXSERVICE::pfnOption */
104static DECLCALLBACK(int) VBoxServiceControlOption(const char **ppszShort, int argc, char **argv, int *pi)
105{
106 int rc = -1;
107 if (ppszShort)
108 /* no short options */;
109 else if (!strcmp(argv[*pi], "--control-interval"))
110 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
111 &g_cMsControlInterval, 1, UINT32_MAX - 1);
112 else if (!strcmp(argv[*pi], "--control-procs-max-kept"))
113 rc = VBoxServiceArgUInt32(argc, argv, "", pi,
114 &g_GuestControlProcsMaxKept, 0, UINT32_MAX - 1);
115 return rc;
116}
117
118
119/** @copydoc VBOXSERVICE::pfnInit */
120static DECLCALLBACK(int) VBoxServiceControlInit(void)
121{
122 /*
123 * If not specified, find the right interval default.
124 * Then create the event sem to block on.
125 */
126 if (!g_cMsControlInterval)
127 g_cMsControlInterval = 1000;
128
129 int rc = RTSemEventMultiCreate(&g_hControlEvent);
130 AssertRCReturn(rc, rc);
131
132 rc = VbglR3GuestCtrlConnect(&g_GuestControlSvcClientID);
133 if (RT_SUCCESS(rc))
134 {
135 VBoxServiceVerbose(3, "Control: Service client ID: %#x\n", g_GuestControlSvcClientID);
136
137 /* Init thread list. */
138 RTListInit(&g_GuestControlThreads);
139 rc = RTCritSectInit(&g_GuestControlThreadsCritSect);
140 AssertRC(rc);
141 }
142 else
143 {
144 /* If the service was not found, we disable this service without
145 causing VBoxService to fail. */
146 if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
147 {
148 VBoxServiceVerbose(0, "Control: Guest control service is not available\n");
149 rc = VERR_SERVICE_DISABLED;
150 }
151 else
152 VBoxServiceError("Control: Failed to connect to the guest control service! Error: %Rrc\n", rc);
153 RTSemEventMultiDestroy(g_hControlEvent);
154 g_hControlEvent = NIL_RTSEMEVENTMULTI;
155 }
156 return rc;
157}
158
159
160/** @copydoc VBOXSERVICE::pfnWorker */
161DECLCALLBACK(int) VBoxServiceControlWorker(bool volatile *pfShutdown)
162{
163 /*
164 * Tell the control thread that it can continue
165 * spawning services.
166 */
167 RTThreadUserSignal(RTThreadSelf());
168 Assert(g_GuestControlSvcClientID > 0);
169
170 int rc = VINF_SUCCESS;
171
172 /*
173 * Execution loop.
174 *
175 * @todo
176 */
177 for (;;)
178 {
179 VBoxServiceVerbose(3, "Control: Waiting for host msg ...\n");
180 uint32_t uMsg;
181 uint32_t cParms;
182 rc = VbglR3GuestCtrlWaitForHostMsg(g_GuestControlSvcClientID, &uMsg, &cParms);
183 if (rc == VERR_TOO_MUCH_DATA)
184 {
185 VBoxServiceVerbose(4, "Control: Message requires %ld parameters, but only 2 supplied -- retrying request (no error!)...\n", cParms);
186 rc = VINF_SUCCESS; /* Try to get "real" message in next block below. */
187 }
188 else if (RT_FAILURE(rc))
189 VBoxServiceVerbose(3, "Control: Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
190 if (RT_SUCCESS(rc))
191 {
192 VBoxServiceVerbose(3, "Control: Msg=%u (%u parms) retrieved\n", uMsg, cParms);
193 switch (uMsg)
194 {
195 case HOST_CANCEL_PENDING_WAITS:
196 VBoxServiceVerbose(3, "Control: Host asked us to quit ...\n");
197 break;
198
199 case HOST_EXEC_CMD:
200 rc = VBoxServiceControlHandleCmdStartProc(g_GuestControlSvcClientID, cParms);
201 break;
202
203 case HOST_EXEC_SET_INPUT:
204 /** @todo Make buffer size configurable via guest properties/argv! */
205 rc = VBoxServiceControlHandleCmdSetInput(g_GuestControlSvcClientID, cParms, _1M /* Buffer size */);
206 break;
207
208 case HOST_EXEC_GET_OUTPUT:
209 rc = VBoxServiceControlHandleCmdGetOutput(g_GuestControlSvcClientID, cParms);
210 break;
211
212 default:
213 VBoxServiceVerbose(3, "Control: Unsupported message from host! Msg=%u\n", uMsg);
214 /* Don't terminate here; just wait for the next message. */
215 break;
216 }
217 }
218
219 /* Do we need to shutdown? */
220 if ( *pfShutdown
221 || uMsg == HOST_CANCEL_PENDING_WAITS)
222 {
223 rc = VINF_SUCCESS;
224 break;
225 }
226
227 /* Let's sleep for a bit and let others run ... */
228 RTThreadYield();
229 }
230
231 return rc;
232}
233
234
235/**
236 * Handles starting processes on the guest.
237 *
238 * @returns IPRT status code.
239 * @param idClient The HGCM client session ID.
240 * @param cParms The number of parameters the host is offering.
241 */
242static int VBoxServiceControlHandleCmdStartProc(uint32_t idClient, uint32_t cParms)
243{
244 uint32_t uContextID;
245 char szCmd[_1K];
246 uint32_t uFlags;
247 char szArgs[_1K];
248 uint32_t cArgs;
249 char szEnv[_64K];
250 uint32_t cbEnv = sizeof(szEnv);
251 uint32_t cEnvVars;
252 char szUser[128];
253 char szPassword[128];
254 uint32_t uTimeLimitMS;
255
256#if 0 /* for valgrind */
257 RT_ZERO(szCmd);
258 RT_ZERO(szArgs);
259 RT_ZERO(szEnv);
260 RT_ZERO(szUser);
261 RT_ZERO(szPassword);
262#endif
263
264 int rc;
265 bool fStartAllowed = false; /* Flag indicating whether starting a process is allowed or not. */
266 if (cParms == 11)
267 {
268 rc = VbglR3GuestCtrlExecGetHostCmdExec(idClient,
269 cParms,
270 &uContextID,
271 /* Command */
272 szCmd, sizeof(szCmd),
273 /* Flags */
274 &uFlags,
275 /* Arguments */
276 szArgs, sizeof(szArgs), &cArgs,
277 /* Environment */
278 szEnv, &cbEnv, &cEnvVars,
279 /* Credentials */
280 szUser, sizeof(szUser),
281 szPassword, sizeof(szPassword),
282 /* Timelimit */
283 &uTimeLimitMS);
284 if (RT_SUCCESS(rc))
285 {
286 VBoxServiceVerbose(3, "Control: Request to start process szCmd=%s, uFlags=0x%x, szArgs=%s, szEnv=%s, szUser=%s, uTimeout=%u\n",
287 szCmd, uFlags, cArgs ? szArgs : "<None>", cEnvVars ? szEnv : "<None>", szUser, uTimeLimitMS);
288 rc = VBoxServiceControlStartAllowed(&fStartAllowed);
289 if (RT_SUCCESS(rc))
290 {
291 if (fStartAllowed)
292 {
293 rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
294 if (RT_SUCCESS(rc))
295 {
296 /** @todo Put the following params into a struct! */
297 PRTLISTNODE pThreadNode;
298 rc = VBoxServiceControlThreadStart(idClient, uContextID,
299 szCmd, uFlags, szArgs, cArgs,
300 szEnv, cbEnv, cEnvVars,
301 szUser, szPassword, uTimeLimitMS,
302 &pThreadNode);
303 if (RT_SUCCESS(rc))
304 {
305 /* Insert thread node into thread list. */
306 /*rc =*/ RTListAppend(&g_GuestControlThreads, pThreadNode);
307 }
308
309 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
310 if (RT_SUCCESS(rc))
311 rc = rc2;
312 }
313 }
314 else
315 rc = VERR_MAX_PROCS_REACHED; /* Maximum number of processes reached. */
316 }
317 }
318 }
319 else
320 rc = VERR_INVALID_PARAMETER; /* Incorrect number of parameters. */
321
322 /* In case of an error we need to notify the host to not wait forever for our response. */
323 if (RT_FAILURE(rc))
324 {
325 VBoxServiceError("Control: Starting process failed with rc=%Rrc\n", rc);
326
327 int rc2 = VbglR3GuestCtrlExecReportStatus(idClient, uContextID, 0 /* PID, invalid. */,
328 PROC_STS_ERROR, rc,
329 NULL /* pvData */, 0 /* cbData */);
330 if (RT_FAILURE(rc2))
331 {
332 VBoxServiceError("Control: Error sending start process status to host, rc=%Rrc\n", rc2);
333 if (RT_SUCCESS(rc))
334 rc = rc2;
335 }
336 }
337
338 return rc;
339}
340
341
342/**
343 * Gets output from stdout/stderr of a specified guest process.
344 *
345 * @return IPRT status code.
346 * @param uPID PID of process to retrieve the output from.
347 * @param uHandleId Stream ID (stdout = 0, stderr = 2) to get the output from.
348 * @param uTimeout Timeout (in ms) to wait for output becoming available.
349 * @param pvBuf Pointer to a pre-allocated buffer to store the output.
350 * @param cbBuf Size (in bytes) of the pre-allocated buffer.
351 * @param pcbRead Pointer to number of bytes read. Optional.
352 */
353int VBoxServiceControlExecGetOutput(uint32_t uPID, uint32_t uCID,
354 uint32_t uHandleId, uint32_t uTimeout,
355 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
356{
357 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
358 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
359 /* pcbRead is optional. */
360
361 int rc = VINF_SUCCESS;
362 VBOXSERVICECTRLREQUESTTYPE reqType;
363 switch (uHandleId)
364 {
365 case OUTPUT_HANDLE_ID_STDERR:
366 reqType = VBOXSERVICECTRLREQUEST_STDERR_READ;
367 break;
368
369 case OUTPUT_HANDLE_ID_STDOUT:
370 case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED:
371 reqType = VBOXSERVICECTRLREQUEST_STDOUT_READ;
372 break;
373
374 default:
375 rc = VERR_INVALID_PARAMETER;
376 break;
377 }
378
379 PVBOXSERVICECTRLREQUEST pRequest;
380 if (RT_SUCCESS(rc))
381 {
382 rc = VBoxServiceControlThreadRequestAllocEx(&pRequest, reqType,
383 pvBuf, cbBuf, uCID);
384 if (RT_SUCCESS(rc))
385 rc = VBoxServiceControlThreadPerform(uPID, pRequest);
386
387 if (RT_SUCCESS(rc))
388 {
389 if (pcbRead)
390 *pcbRead = pRequest->cbData;
391 }
392
393 VBoxServiceControlThreadRequestFree(pRequest);
394 }
395
396 return rc;
397}
398
399
400/**
401 * Injects input to a specified running process.
402 *
403 * @return IPRT status code.
404 * @param uPID PID of process to set the input for.
405 * @param fPendingClose Flag indicating whether this is the last input block sent to the process.
406 * @param pvBuf Pointer to a buffer containing the actual input data.
407 * @param cbBuf Size (in bytes) of the input buffer data.
408 * @param pcbWritten Pointer to number of bytes written to the process. Optional.
409 */
410int VBoxServiceControlSetInput(uint32_t uPID, uint32_t uCID,
411 bool fPendingClose,
412 void *pvBuf, uint32_t cbBuf,
413 uint32_t *pcbWritten)
414{
415 AssertPtrReturn(pvBuf, VERR_INVALID_PARAMETER);
416 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
417 /* pcbWritten is optional. */
418
419 PVBOXSERVICECTRLREQUEST pRequest;
420 int rc = VBoxServiceControlThreadRequestAllocEx(&pRequest,
421 fPendingClose
422 ? VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF
423 : VBOXSERVICECTRLREQUEST_STDIN_WRITE,
424 pvBuf, cbBuf, uCID);
425 if (RT_SUCCESS(rc))
426 {
427 rc = VBoxServiceControlThreadPerform(uPID, pRequest);
428 if (RT_SUCCESS(rc))
429 {
430 if (pcbWritten)
431 *pcbWritten = pRequest->cbData;
432 }
433
434 VBoxServiceControlThreadRequestFree(pRequest);
435 }
436
437 return rc;
438}
439
440
441/**
442 * Handles input for a started process by copying the received data into its
443 * stdin pipe.
444 *
445 * @returns IPRT status code.
446 * @param idClient The HGCM client session ID.
447 * @param cParms The number of parameters the host is
448 * offering.
449 * @param cMaxBufSize The maximum buffer size for retrieving the input data.
450 */
451static int VBoxServiceControlHandleCmdSetInput(uint32_t idClient, uint32_t cParms, size_t cbMaxBufSize)
452{
453 uint32_t uContextID;
454 uint32_t uPID;
455 uint32_t uFlags;
456 uint32_t cbSize;
457
458 AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER);
459 uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize);
460 AssertPtrReturn(pabBuffer, VERR_NO_MEMORY);
461
462 uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */
463 uint32_t cbWritten = 0; /* Number of bytes written to the guest. */
464
465 /*
466 * Ask the host for the input data.
467 */
468 int rc = VbglR3GuestCtrlExecGetHostCmdInput(idClient, cParms,
469 &uContextID, &uPID, &uFlags,
470 pabBuffer, cbMaxBufSize, &cbSize);
471 if (RT_FAILURE(rc))
472 {
473 VBoxServiceError("Control: [PID %u]: Failed to retrieve exec input command! Error: %Rrc\n",
474 uPID, rc);
475 }
476 else if (cbSize > cbMaxBufSize)
477 {
478 VBoxServiceError("Control: [PID %u]: Too much input received! cbSize=%u, cbMaxBufSize=%u\n",
479 uPID, cbSize, cbMaxBufSize);
480 rc = VERR_INVALID_PARAMETER;
481 }
482 else
483 {
484 /*
485 * Is this the last input block we need to deliver? Then let the pipe know ...
486 */
487 bool fPendingClose = false;
488 if (uFlags & INPUT_FLAG_EOF)
489 {
490 fPendingClose = true;
491 VBoxServiceVerbose(4, "Control: [PID %u]: Got last input block of size %u ...\n",
492 uPID, cbSize);
493 }
494
495 rc = VBoxServiceControlSetInput(uPID, uContextID, fPendingClose, pabBuffer,
496 cbSize, &cbWritten);
497 VBoxServiceVerbose(4, "Control: [PID %u]: Written input, CID=%u, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
498 uPID, uContextID, rc, uFlags, fPendingClose, cbSize, cbWritten);
499 if (RT_SUCCESS(rc))
500 {
501 if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */
502 {
503 uStatus = INPUT_STS_WRITTEN;
504 uFlags = 0;
505 }
506 }
507 else
508 {
509 if (rc == VERR_BAD_PIPE)
510 uStatus = INPUT_STS_TERMINATED;
511 else if (rc == VERR_BUFFER_OVERFLOW)
512 uStatus = INPUT_STS_OVERFLOW;
513 }
514 }
515 RTMemFree(pabBuffer);
516
517 /*
518 * If there was an error and we did not set the host status
519 * yet, then do it now.
520 */
521 if ( RT_FAILURE(rc)
522 && uStatus == INPUT_STS_UNDEFINED)
523 {
524 uStatus = INPUT_STS_ERROR;
525 uFlags = rc;
526 }
527 Assert(uStatus > INPUT_STS_UNDEFINED);
528
529 VBoxServiceVerbose(3, "Control: [PID %u]: Input processed, CID=%u, uStatus=%u, uFlags=0x%x, cbWritten=%u\n",
530 uPID, uContextID, uStatus, uFlags, cbWritten);
531
532 /* Note: Since the context ID is unique the request *has* to be completed here,
533 * regardless whether we got data or not! Otherwise the progress object
534 * on the host never will get completed! */
535 rc = VbglR3GuestCtrlExecReportStatusIn(idClient, uContextID, uPID,
536 uStatus, uFlags, (uint32_t)cbWritten);
537
538 if (RT_FAILURE(rc))
539 VBoxServiceError("Control: [PID %u]: Failed to report input status! Error: %Rrc\n",
540 uPID, rc);
541 return rc;
542}
543
544
545/**
546 * Handles the guest control output command.
547 *
548 * @return IPRT status code.
549 * @param idClient The HGCM client session ID.
550 * @param cParms The number of parameters the host is offering.
551 */
552static int VBoxServiceControlHandleCmdGetOutput(uint32_t idClient, uint32_t cParms)
553{
554 uint32_t uContextID;
555 uint32_t uPID;
556 uint32_t uHandleID;
557 uint32_t uFlags;
558
559 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(idClient, cParms,
560 &uContextID, &uPID, &uHandleID, &uFlags);
561 if (RT_SUCCESS(rc))
562 {
563 uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K);
564 if (pBuf)
565 {
566 uint32_t cbRead = 0;
567 rc = VBoxServiceControlExecGetOutput(uPID, uContextID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
568 pBuf, _64K /* cbSize */, &cbRead);
569 VBoxServiceVerbose(3, "Control: Got output returned with rc=%Rrc (PID=%u, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u)\n",
570 rc, uPID, uContextID, cbRead, uHandleID, uFlags);
571
572 /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary
573 * data which the host needs to work with -- so just pass through all data unfiltered! */
574
575 /* Note: Since the context ID is unique the request *has* to be completed here,
576 * regardless whether we got data or not! Otherwise the progress object
577 * on the host never will get completed! */
578 int rc2 = VbglR3GuestCtrlExecSendOut(idClient, uContextID, uPID, uHandleID, uFlags,
579 pBuf, cbRead);
580 if (RT_SUCCESS(rc))
581 rc = rc2;
582 RTMemFree(pBuf);
583 }
584 else
585 rc = VERR_NO_MEMORY;
586 }
587
588 if (RT_FAILURE(rc))
589 VBoxServiceError("Control: [PID %u]: Error handling output command! Error: %Rrc\n",
590 uPID, rc);
591 return rc;
592}
593
594
595/** @copydoc VBOXSERVICE::pfnStop */
596static DECLCALLBACK(void) VBoxServiceControlStop(void)
597{
598 VBoxServiceVerbose(3, "Control: Stopping ...\n");
599
600 /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
601 * annoying call since doesn't support timeouts in the posix world. */
602 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
603 RTSemEventMultiSignal(g_hControlEvent);
604
605 /*
606 * Ask the host service to cancel all pending requests so that we can
607 * shutdown properly here.
608 */
609 if (g_GuestControlSvcClientID)
610 {
611 VBoxServiceVerbose(3, "Control: Cancelling pending waits (client ID=%u) ...\n",
612 g_GuestControlSvcClientID);
613
614 int rc = VbglR3GuestCtrlCancelPendingWaits(g_GuestControlSvcClientID);
615 if (RT_FAILURE(rc))
616 VBoxServiceError("Control: Cancelling pending waits failed; rc=%Rrc\n", rc);
617 }
618}
619
620
621/**
622 * Destroys all guest process threads which are still active.
623 */
624static void VBoxServiceControlDestroyThreads(void)
625{
626 VBoxServiceVerbose(2, "Control: Destroying threads ...\n");
627
628 /* Signal all threads that we want to shutdown. */
629 PVBOXSERVICECTRLTHREAD pThread;
630 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
631 VBoxServiceControlThreadSignalShutdown(pThread);
632
633 /* Wait for threads to shutdown and destroy thread list. */
634 pThread = RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node);
635 while (pThread)
636 {
637 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node);
638 bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &pThread->Node);
639
640 int rc2 = VBoxServiceControlThreadWaitForShutdown(pThread,
641 30 * 1000 /* Wait 30 seconds max. */);
642 if (RT_FAILURE(rc2))
643 VBoxServiceError("Control: Guest process thread failed to stop; rc=%Rrc\n", rc2);
644
645 if (fLast)
646 break;
647
648 pThread = pNext;
649 }
650
651#ifdef DEBUG
652 PVBOXSERVICECTRLTHREAD pThreadCur;
653 uint32_t cThreads = 0;
654 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
655 cThreads++;
656 VBoxServiceVerbose(4, "Control: Guest process threads left=%u\n", cThreads);
657#endif
658 AssertMsg(RTListIsEmpty(&g_GuestControlThreads),
659 ("Guest process thread list still contains children when it should not\n"));
660
661 /* Destroy critical section. */
662 RTCritSectDelete(&g_GuestControlThreadsCritSect);
663}
664
665
666/** @copydoc VBOXSERVICE::pfnTerm */
667static DECLCALLBACK(void) VBoxServiceControlTerm(void)
668{
669 VBoxServiceVerbose(3, "Control: Terminating ...\n");
670
671 VBoxServiceControlDestroyThreads();
672
673 VBoxServiceVerbose(3, "Control: Disconnecting client ID=%u ...\n",
674 g_GuestControlSvcClientID);
675 VbglR3GuestCtrlDisconnect(g_GuestControlSvcClientID);
676 g_GuestControlSvcClientID = 0;
677
678 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
679 {
680 RTSemEventMultiDestroy(g_hControlEvent);
681 g_hControlEvent = NIL_RTSEMEVENTMULTI;
682 }
683}
684
685
686/**
687 * Determines whether starting a new guest process according to the
688 * maximum number of concurrent guest processes defined is allowed or not.
689 *
690 * @return IPRT status code.
691 * @param pbAllowed True if starting (another) guest process
692 * is allowed, false if not.
693 */
694static int VBoxServiceControlStartAllowed(bool *pbAllowed)
695{
696 AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
697
698 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
699 if (RT_SUCCESS(rc))
700 {
701 /*
702 * Check if we're respecting our memory policy by checking
703 * how many guest processes are started and served already.
704 */
705 bool fLimitReached = false;
706 if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
707 {
708 uint32_t uProcsRunning = 0;
709 uint32_t uProcsStopped = 0;
710 PVBOXSERVICECTRLTHREAD pThread;
711 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
712 {
713 VBoxServiceControlThreadActive(pThread)
714 ? uProcsRunning++
715 : uProcsStopped++;
716 }
717
718 VBoxServiceVerbose(3, "Control: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
719 g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped);
720
721 int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1);
722 if (iProcsLeft < 0)
723 {
724 VBoxServiceVerbose(3, "Control: Maximum running guest processes reached (%u)\n",
725 g_GuestControlProcsMaxKept);
726 fLimitReached = true;
727 }
728 }
729
730 *pbAllowed = !fLimitReached;
731
732 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
733 if (RT_SUCCESS(rc))
734 rc = rc2;
735 }
736
737 return rc;
738}
739
740
741/**
742 * Finds a (formerly) started process given by its PID and locks it. Must be unlocked
743 * by the caller with VBoxServiceControlThreadUnlock().
744 *
745 * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL.
746 * @param uPID PID to search for.
747 */
748PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadLocked(uint32_t uPID)
749{
750 PVBOXSERVICECTRLTHREAD pThread = NULL;
751 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
752 if (RT_SUCCESS(rc))
753 {
754 PVBOXSERVICECTRLTHREAD pThreadCur;
755 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
756 {
757 if (pThreadCur->uPID == uPID)
758 {
759 rc = RTCritSectEnter(&pThreadCur->CritSect);
760 if (RT_SUCCESS(rc))
761 pThread = pThreadCur;
762 break;
763 }
764 }
765
766 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
767 if (RT_SUCCESS(rc))
768 rc = rc2;
769 }
770
771 return pThread;
772}
773
774
775/**
776 * Unlocks a previously locked guest process thread.
777 *
778 * @param pThread Thread to unlock.
779 */
780void VBoxServiceControlThreadUnlock(const PVBOXSERVICECTRLTHREAD pThread)
781{
782 AssertPtr(pThread);
783
784 int rc = RTCritSectLeave(&pThread->CritSect);
785 AssertRC(rc);
786}
787
788
789/**
790 * Assigns a valid PID to a guest control thread and also checks if there already was
791 * another (stale) guest process which was using that PID before and destroys it.
792 *
793 * @return IPRT status code.
794 * @param pThread Thread to assign PID to.
795 * @param uPID PID to assign to the specified guest control execution thread.
796 */
797int VBoxServiceControlAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID)
798{
799 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
800 AssertReturn(uPID, VERR_INVALID_PARAMETER);
801
802 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
803 if (RT_SUCCESS(rc))
804 {
805 /* Search old threads using the desired PID and shut them down completely -- it's
806 * not used anymore. */
807 PVBOXSERVICECTRLTHREAD pThreadCur;
808 bool fTryAgain = false;
809 do
810 {
811 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
812 {
813 if (pThreadCur->uPID == uPID)
814 {
815 Assert(pThreadCur != pThread); /* can't happen */
816 uint32_t uTriedPID = uPID;
817 uPID += 391939;
818 VBoxServiceVerbose(2, "ControlThread: PID %u was used before, trying again with %u ...\n",
819 uTriedPID, uPID);
820 fTryAgain = true;
821 break;
822 }
823 }
824 } while (fTryAgain);
825
826 /* Assign PID to current thread. */
827 pThread->uPID = uPID;
828
829 rc = RTCritSectLeave(&g_GuestControlThreadsCritSect);
830 AssertRC(rc);
831 }
832
833 return rc;
834}
835
836
837/**
838 * Removes the specified guest process thread from the global thread
839 * list.
840 *
841 * @return IPRT status code.
842 * @param pThread Thread to remove.
843 */
844void VBoxServiceControlRemoveThread(const PVBOXSERVICECTRLTHREAD pThread)
845{
846 if (!pThread)
847 return;
848
849 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
850 if (RT_SUCCESS(rc))
851 {
852 VBoxServiceVerbose(4, "Control: Removing thread (PID: %u) from thread list\n",
853 pThread->uPID);
854 RTListNodeRemove(&pThread->Node);
855
856#ifdef DEBUG
857 PVBOXSERVICECTRLTHREAD pThreadCur;
858 uint32_t cThreads = 0;
859 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
860 cThreads++;
861 VBoxServiceVerbose(4, "Control: Guest process threads left=%u\n", cThreads);
862#endif
863 rc = RTCritSectLeave(&g_GuestControlThreadsCritSect);
864 AssertRC(rc);
865 }
866}
867
868
869/**
870 * The 'vminfo' service description.
871 */
872VBOXSERVICE g_Control =
873{
874 /* pszName. */
875 "control",
876 /* pszDescription. */
877 "Host-driven Guest Control",
878 /* pszUsage. */
879 " [--control-interval <ms>] [--control-procs-max-kept <x>]\n"
880 " [--control-procs-mem-std[in|out|err] <KB>]"
881 ,
882 /* pszOptions. */
883 " --control-interval Specifies the interval at which to check for\n"
884 " new control commands. The default is 1000 ms.\n"
885 " --control-procs-max-kept\n"
886 " Specifies how many started guest processes are\n"
887 " kept into memory to work with. Default is 25.\n"
888 ,
889 /* methods */
890 VBoxServiceControlPreInit,
891 VBoxServiceControlOption,
892 VBoxServiceControlInit,
893 VBoxServiceControlWorker,
894 VBoxServiceControlStop,
895 VBoxServiceControlTerm
896};
897
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