VirtualBox

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

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

VBoxServiceControl: Be more quiet on not found guest processes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.0 KB
Line 
1/* $Id: VBoxServiceControl.cpp 39874 2012-01-25 11:59:08Z 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.215389.xyz. 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: [PID %u]: Got output, rc=%Rrc, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
570 uPID, rc, 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 else if (rc == VERR_NOT_FOUND) /* It's not critical if guest process (PID) is not found. */
583 rc = VINF_SUCCESS;
584
585 RTMemFree(pBuf);
586 }
587 else
588 rc = VERR_NO_MEMORY;
589 }
590
591 if (RT_FAILURE(rc))
592 VBoxServiceError("Control: [PID %u]: Error handling output command! Error: %Rrc\n",
593 uPID, rc);
594 return rc;
595}
596
597
598/** @copydoc VBOXSERVICE::pfnStop */
599static DECLCALLBACK(void) VBoxServiceControlStop(void)
600{
601 VBoxServiceVerbose(3, "Control: Stopping ...\n");
602
603 /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
604 * annoying call since doesn't support timeouts in the posix world. */
605 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
606 RTSemEventMultiSignal(g_hControlEvent);
607
608 /*
609 * Ask the host service to cancel all pending requests so that we can
610 * shutdown properly here.
611 */
612 if (g_GuestControlSvcClientID)
613 {
614 VBoxServiceVerbose(3, "Control: Cancelling pending waits (client ID=%u) ...\n",
615 g_GuestControlSvcClientID);
616
617 int rc = VbglR3GuestCtrlCancelPendingWaits(g_GuestControlSvcClientID);
618 if (RT_FAILURE(rc))
619 VBoxServiceError("Control: Cancelling pending waits failed; rc=%Rrc\n", rc);
620 }
621}
622
623
624/**
625 * Destroys all guest process threads which are still active.
626 */
627static void VBoxServiceControlDestroyThreads(void)
628{
629 VBoxServiceVerbose(2, "Control: Destroying threads ...\n");
630
631 /* Signal all threads that we want to shutdown. */
632 PVBOXSERVICECTRLTHREAD pThread;
633 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
634 VBoxServiceControlThreadSignalShutdown(pThread);
635
636 /* Wait for threads to shutdown and destroy thread list. */
637 pThread = RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node);
638 while (pThread)
639 {
640 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node);
641 bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &pThread->Node);
642
643 int rc2 = VBoxServiceControlThreadWaitForShutdown(pThread,
644 30 * 1000 /* Wait 30 seconds max. */);
645 if (RT_FAILURE(rc2))
646 VBoxServiceError("Control: Guest process thread failed to stop; rc=%Rrc\n", rc2);
647
648 if (fLast)
649 break;
650
651 pThread = pNext;
652 }
653
654#ifdef DEBUG
655 PVBOXSERVICECTRLTHREAD pThreadCur;
656 uint32_t cThreads = 0;
657 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
658 cThreads++;
659 VBoxServiceVerbose(4, "Control: Guest process threads left=%u\n", cThreads);
660#endif
661 AssertMsg(RTListIsEmpty(&g_GuestControlThreads),
662 ("Guest process thread list still contains children when it should not\n"));
663
664 /* Destroy critical section. */
665 RTCritSectDelete(&g_GuestControlThreadsCritSect);
666}
667
668
669/** @copydoc VBOXSERVICE::pfnTerm */
670static DECLCALLBACK(void) VBoxServiceControlTerm(void)
671{
672 VBoxServiceVerbose(3, "Control: Terminating ...\n");
673
674 VBoxServiceControlDestroyThreads();
675
676 VBoxServiceVerbose(3, "Control: Disconnecting client ID=%u ...\n",
677 g_GuestControlSvcClientID);
678 VbglR3GuestCtrlDisconnect(g_GuestControlSvcClientID);
679 g_GuestControlSvcClientID = 0;
680
681 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
682 {
683 RTSemEventMultiDestroy(g_hControlEvent);
684 g_hControlEvent = NIL_RTSEMEVENTMULTI;
685 }
686}
687
688
689/**
690 * Determines whether starting a new guest process according to the
691 * maximum number of concurrent guest processes defined is allowed or not.
692 *
693 * @return IPRT status code.
694 * @param pbAllowed True if starting (another) guest process
695 * is allowed, false if not.
696 */
697static int VBoxServiceControlStartAllowed(bool *pbAllowed)
698{
699 AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
700
701 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
702 if (RT_SUCCESS(rc))
703 {
704 /*
705 * Check if we're respecting our memory policy by checking
706 * how many guest processes are started and served already.
707 */
708 bool fLimitReached = false;
709 if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
710 {
711 uint32_t uProcsRunning = 0;
712 uint32_t uProcsStopped = 0;
713 PVBOXSERVICECTRLTHREAD pThread;
714 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
715 {
716 VBoxServiceControlThreadActive(pThread)
717 ? uProcsRunning++
718 : uProcsStopped++;
719 }
720
721 VBoxServiceVerbose(3, "Control: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
722 g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped);
723
724 int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1);
725 if (iProcsLeft < 0)
726 {
727 VBoxServiceVerbose(3, "Control: Maximum running guest processes reached (%u)\n",
728 g_GuestControlProcsMaxKept);
729 fLimitReached = true;
730 }
731 }
732
733 *pbAllowed = !fLimitReached;
734
735 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
736 if (RT_SUCCESS(rc))
737 rc = rc2;
738 }
739
740 return rc;
741}
742
743
744/**
745 * Finds a (formerly) started process given by its PID and locks it. Must be unlocked
746 * by the caller with VBoxServiceControlThreadUnlock().
747 *
748 * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL.
749 * @param uPID PID to search for.
750 */
751PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadLocked(uint32_t uPID)
752{
753 PVBOXSERVICECTRLTHREAD pThread = NULL;
754 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
755 if (RT_SUCCESS(rc))
756 {
757 PVBOXSERVICECTRLTHREAD pThreadCur;
758 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
759 {
760 if (pThreadCur->uPID == uPID)
761 {
762 rc = RTCritSectEnter(&pThreadCur->CritSect);
763 if (RT_SUCCESS(rc))
764 pThread = pThreadCur;
765 break;
766 }
767 }
768
769 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
770 if (RT_SUCCESS(rc))
771 rc = rc2;
772 }
773
774 return pThread;
775}
776
777
778/**
779 * Unlocks a previously locked guest process thread.
780 *
781 * @param pThread Thread to unlock.
782 */
783void VBoxServiceControlThreadUnlock(const PVBOXSERVICECTRLTHREAD pThread)
784{
785 AssertPtr(pThread);
786
787 int rc = RTCritSectLeave(&pThread->CritSect);
788 AssertRC(rc);
789}
790
791
792/**
793 * Assigns a valid PID to a guest control thread and also checks if there already was
794 * another (stale) guest process which was using that PID before and destroys it.
795 *
796 * @return IPRT status code.
797 * @param pThread Thread to assign PID to.
798 * @param uPID PID to assign to the specified guest control execution thread.
799 */
800int VBoxServiceControlAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID)
801{
802 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
803 AssertReturn(uPID, VERR_INVALID_PARAMETER);
804
805 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
806 if (RT_SUCCESS(rc))
807 {
808 /* Search old threads using the desired PID and shut them down completely -- it's
809 * not used anymore. */
810 PVBOXSERVICECTRLTHREAD pThreadCur;
811 bool fTryAgain = false;
812 do
813 {
814 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
815 {
816 if (pThreadCur->uPID == uPID)
817 {
818 Assert(pThreadCur != pThread); /* can't happen */
819 uint32_t uTriedPID = uPID;
820 uPID += 391939;
821 VBoxServiceVerbose(2, "ControlThread: PID %u was used before, trying again with %u ...\n",
822 uTriedPID, uPID);
823 fTryAgain = true;
824 break;
825 }
826 }
827 } while (fTryAgain);
828
829 /* Assign PID to current thread. */
830 pThread->uPID = uPID;
831
832 rc = RTCritSectLeave(&g_GuestControlThreadsCritSect);
833 AssertRC(rc);
834 }
835
836 return rc;
837}
838
839
840/**
841 * Removes the specified guest process thread from the global thread
842 * list.
843 *
844 * @return IPRT status code.
845 * @param pThread Thread to remove.
846 */
847void VBoxServiceControlRemoveThread(const PVBOXSERVICECTRLTHREAD pThread)
848{
849 if (!pThread)
850 return;
851
852 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
853 if (RT_SUCCESS(rc))
854 {
855 VBoxServiceVerbose(4, "Control: Removing thread (PID: %u) from thread list\n",
856 pThread->uPID);
857 RTListNodeRemove(&pThread->Node);
858
859#ifdef DEBUG
860 PVBOXSERVICECTRLTHREAD pThreadCur;
861 uint32_t cThreads = 0;
862 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
863 cThreads++;
864 VBoxServiceVerbose(4, "Control: Guest process threads left=%u\n", cThreads);
865#endif
866 rc = RTCritSectLeave(&g_GuestControlThreadsCritSect);
867 AssertRC(rc);
868 }
869}
870
871
872/**
873 * The 'vminfo' service description.
874 */
875VBOXSERVICE g_Control =
876{
877 /* pszName. */
878 "control",
879 /* pszDescription. */
880 "Host-driven Guest Control",
881 /* pszUsage. */
882 " [--control-interval <ms>] [--control-procs-max-kept <x>]\n"
883 " [--control-procs-mem-std[in|out|err] <KB>]"
884 ,
885 /* pszOptions. */
886 " --control-interval Specifies the interval at which to check for\n"
887 " new control commands. The default is 1000 ms.\n"
888 " --control-procs-max-kept\n"
889 " Specifies how many started guest processes are\n"
890 " kept into memory to work with. Default is 25.\n"
891 ,
892 /* methods */
893 VBoxServiceControlPreInit,
894 VBoxServiceControlOption,
895 VBoxServiceControlInit,
896 VBoxServiceControlWorker,
897 VBoxServiceControlStop,
898 VBoxServiceControlTerm
899};
900
Note: See TracBrowser for help on using the repository browser.

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