VirtualBox

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

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

GuestControl: Bugfixes, logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.3 KB
Line 
1/* $Id: VBoxServiceControl.cpp 39659 2011-12-20 10:36:53Z 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
363 VBOXSERVICECTRLREQUEST ctrlRequest;
364 ctrlRequest.uCID = uCID;
365 ctrlRequest.cbData = cbBuf;
366 ctrlRequest.pvData = (uint8_t*)pvBuf;
367
368 switch (uHandleId)
369 {
370 case OUTPUT_HANDLE_ID_STDERR:
371 ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_STDERR_READ;
372 break;
373
374 case OUTPUT_HANDLE_ID_STDOUT:
375 case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED:
376 ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_STDOUT_READ;
377 break;
378
379 default:
380 rc = VERR_INVALID_PARAMETER;
381 break;
382 }
383
384 if (RT_SUCCESS(rc))
385 rc = VBoxServiceControlThreadPerform(uPID, &ctrlRequest);
386
387 if (RT_SUCCESS(rc))
388 {
389 if (pcbRead)
390 *pcbRead = ctrlRequest.cbData;
391 }
392 else /* Something went wrong, nothing read. */
393 *pcbRead = 0;
394
395 return rc;
396}
397
398
399/**
400 * Injects input to a specified running process.
401 *
402 * @return IPRT status code.
403 * @param uPID PID of process to set the input for.
404 * @param fPendingClose Flag indicating whether this is the last input block sent to the process.
405 * @param pvBuf Pointer to a buffer containing the actual input data.
406 * @param cbBuf Size (in bytes) of the input buffer data.
407 * @param pcbWritten Pointer to number of bytes written to the process. Optional.
408 */
409int VBoxServiceControlSetInput(uint32_t uPID, uint32_t uCID,
410 bool fPendingClose,
411 void *pvBuf, uint32_t cbBuf,
412 uint32_t *pcbWritten)
413{
414 AssertPtrReturn(pvBuf, VERR_INVALID_PARAMETER);
415 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
416 /* pcbWritten is optional. */
417
418 int rc = VINF_SUCCESS;
419
420 VBOXSERVICECTRLREQUEST ctrlRequest;
421 ctrlRequest.uCID = uCID;
422 ctrlRequest.cbData = cbBuf;
423 ctrlRequest.pvData = pvBuf;
424 ctrlRequest.enmType = fPendingClose
425 ? VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF : VBOXSERVICECTRLREQUEST_STDIN_WRITE;
426 if (RT_SUCCESS(rc))
427 rc = VBoxServiceControlThreadPerform(uPID, &ctrlRequest);
428
429 if (RT_SUCCESS(rc))
430 {
431 if (pcbWritten)
432 *pcbWritten = ctrlRequest.cbData;
433 }
434
435 return rc;
436}
437
438
439/**
440 * Handles input for a started process by copying the received data into its
441 * stdin pipe.
442 *
443 * @returns IPRT status code.
444 * @param idClient The HGCM client session ID.
445 * @param cParms The number of parameters the host is
446 * offering.
447 * @param cMaxBufSize The maximum buffer size for retrieving the input data.
448 */
449static int VBoxServiceControlHandleCmdSetInput(uint32_t idClient, uint32_t cParms, size_t cbMaxBufSize)
450{
451 uint32_t uContextID;
452 uint32_t uPID;
453 uint32_t uFlags;
454 uint32_t cbSize;
455
456 AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER);
457 uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize);
458 AssertPtrReturn(pabBuffer, VERR_NO_MEMORY);
459
460 uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */
461 uint32_t cbWritten = 0; /* Number of bytes written to the guest. */
462
463 /*
464 * Ask the host for the input data.
465 */
466 int rc = VbglR3GuestCtrlExecGetHostCmdInput(idClient, cParms,
467 &uContextID, &uPID, &uFlags,
468 pabBuffer, cbMaxBufSize, &cbSize);
469 if (RT_FAILURE(rc))
470 {
471 VBoxServiceError("Control: [PID %u]: Failed to retrieve exec input command! Error: %Rrc\n",
472 uPID, rc);
473 }
474 else if (cbSize > cbMaxBufSize)
475 {
476 VBoxServiceError("Control: [PID %u]: Too much input received! cbSize=%u, cbMaxBufSize=%u\n",
477 uPID, cbSize, cbMaxBufSize);
478 rc = VERR_INVALID_PARAMETER;
479 }
480 else
481 {
482 /*
483 * Is this the last input block we need to deliver? Then let the pipe know ...
484 */
485 bool fPendingClose = false;
486 if (uFlags & INPUT_FLAG_EOF)
487 {
488 fPendingClose = true;
489 VBoxServiceVerbose(4, "Control: [PID %u]: Got last input block of size %u ...\n",
490 uPID, cbSize);
491 }
492
493 rc = VBoxServiceControlSetInput(uPID, uContextID, fPendingClose, pabBuffer,
494 cbSize, &cbWritten);
495 VBoxServiceVerbose(4, "Control: [PID %u]: Written input, CID=%u, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
496 uPID, uContextID, rc, uFlags, fPendingClose, cbSize, cbWritten);
497 if (RT_SUCCESS(rc))
498 {
499 if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */
500 {
501 uStatus = INPUT_STS_WRITTEN;
502 uFlags = 0;
503 }
504 }
505 else
506 {
507 if (rc == VERR_BAD_PIPE)
508 uStatus = INPUT_STS_TERMINATED;
509 else if (rc == VERR_BUFFER_OVERFLOW)
510 uStatus = INPUT_STS_OVERFLOW;
511 }
512 }
513 RTMemFree(pabBuffer);
514
515 /*
516 * If there was an error and we did not set the host status
517 * yet, then do it now.
518 */
519 if ( RT_FAILURE(rc)
520 && uStatus == INPUT_STS_UNDEFINED)
521 {
522 uStatus = INPUT_STS_ERROR;
523 uFlags = rc;
524 }
525 Assert(uStatus > INPUT_STS_UNDEFINED);
526
527 VBoxServiceVerbose(3, "Control: [PID %u]: Input processed, CID=%u, uStatus=%u, uFlags=0x%x, cbWritten=%u\n",
528 uPID, uContextID, uStatus, uFlags, cbWritten);
529
530 /* Note: Since the context ID is unique the request *has* to be completed here,
531 * regardless whether we got data or not! Otherwise the progress object
532 * on the host never will get completed! */
533 rc = VbglR3GuestCtrlExecReportStatusIn(idClient, uContextID, uPID,
534 uStatus, uFlags, (uint32_t)cbWritten);
535
536 if (RT_FAILURE(rc))
537 VBoxServiceError("Control: [PID %u]: Failed to report input status! Error: %Rrc\n",
538 uPID, rc);
539 return rc;
540}
541
542
543/**
544 * Handles the guest control output command.
545 *
546 * @return IPRT status code.
547 * @param idClient The HGCM client session ID.
548 * @param cParms The number of parameters the host is offering.
549 */
550static int VBoxServiceControlHandleCmdGetOutput(uint32_t idClient, uint32_t cParms)
551{
552 uint32_t uContextID;
553 uint32_t uPID;
554 uint32_t uHandleID;
555 uint32_t uFlags;
556
557 int rc = VbglR3GuestCtrlExecGetHostCmdOutput(idClient, cParms,
558 &uContextID, &uPID, &uHandleID, &uFlags);
559 if (RT_SUCCESS(rc))
560 {
561 uint32_t cbRead = 0;
562 uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K);
563 if (pBuf)
564 {
565 rc = VBoxServiceControlExecGetOutput(uPID, uContextID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
566 pBuf, _64K /* cbSize */, &cbRead);
567
568 /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary
569 * data which the host needs to work with -- so just pass through all data unfiltered! */
570
571 if (RT_SUCCESS(rc))
572 VBoxServiceVerbose(2, "Control: Got output, PID=%u, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
573 uPID, uContextID, cbRead, uHandleID, uFlags);
574 else if (rc == VERR_NOT_FOUND)
575 VBoxServiceVerbose(2, "Control: PID=%u not found, CID=%u, uHandle=%u\n",
576 uPID, uContextID, uHandleID, rc);
577 else
578 VBoxServiceError("Control: Failed to retrieve output for PID=%u, CID=%u, uHandle=%u, rc=%Rrc\n",
579 uPID, uContextID, uHandleID, rc);
580 /* Note: Since the context ID is unique the request *has* to be completed here,
581 * regardless whether we got data or not! Otherwise the progress object
582 * on the host never will get completed! */
583 /* cbRead now contains actual size. */
584 int rc2 = VbglR3GuestCtrlExecSendOut(idClient, uContextID, uPID, uHandleID, uFlags,
585 pBuf, cbRead);
586 if (RT_SUCCESS(rc))
587 rc = rc2;
588 RTMemFree(pBuf);
589 }
590 else
591 rc = VERR_NO_MEMORY;
592 }
593
594 if (RT_FAILURE(rc))
595 VBoxServiceError("Control: [PID %u]: Error handling output command! Error: %Rrc\n",
596 uPID, rc);
597 return rc;
598}
599
600
601/** @copydoc VBOXSERVICE::pfnStop */
602static DECLCALLBACK(void) VBoxServiceControlStop(void)
603{
604 VBoxServiceVerbose(3, "Control: Stopping ...\n");
605
606 /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
607 * annoying call since doesn't support timeouts in the posix world. */
608 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
609 RTSemEventMultiSignal(g_hControlEvent);
610
611 /*
612 * Ask the host service to cancel all pending requests so that we can
613 * shutdown properly here.
614 */
615 if (g_GuestControlSvcClientID)
616 {
617 VBoxServiceVerbose(3, "Control: Cancelling pending waits (client ID=%u) ...\n",
618 g_GuestControlSvcClientID);
619
620 int rc = VbglR3GuestCtrlCancelPendingWaits(g_GuestControlSvcClientID);
621 if (RT_FAILURE(rc))
622 VBoxServiceError("Control: Cancelling pending waits failed; rc=%Rrc\n", rc);
623 }
624}
625
626
627/**
628 * Destroys all guest process threads which are still active.
629 */
630static void VBoxServiceControlDestroyThreads(void)
631{
632 VBoxServiceVerbose(2, "Control: Destroying threads ...\n");
633
634 /* Signal all threads that we want to shutdown. */
635 PVBOXSERVICECTRLTHREAD pThread;
636 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
637 VBoxServiceControlThreadSignalShutdown(pThread);
638
639 /* Wait for threads to shutdown and destroy thread list. */
640 pThread = RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node);
641 while (pThread)
642 {
643 PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node);
644 bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &pThread->Node);
645
646 int rc2 = VBoxServiceControlThreadWaitForShutdown(pThread,
647 30 * 1000 /* Wait 30 seconds max. */);
648 if (RT_FAILURE(rc2))
649 VBoxServiceError("Control: Guest process thread failed to stop; rc=%Rrc\n", rc2);
650
651 if (fLast)
652 break;
653
654 pThread = pNext;
655 }
656
657#ifdef DEBUG
658 PVBOXSERVICECTRLTHREAD pThreadCur;
659 uint32_t cThreads = 0;
660 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
661 cThreads++;
662 VBoxServiceVerbose(4, "Control: Guest process threads left=%u\n", cThreads);
663#endif
664 AssertMsg(RTListIsEmpty(&g_GuestControlThreads),
665 ("Guest process thread list still contains children when it should not\n"));
666
667 /* Destroy critical section. */
668 RTCritSectDelete(&g_GuestControlThreadsCritSect);
669}
670
671
672/** @copydoc VBOXSERVICE::pfnTerm */
673static DECLCALLBACK(void) VBoxServiceControlTerm(void)
674{
675 VBoxServiceVerbose(3, "Control: Terminating ...\n");
676
677 VBoxServiceControlDestroyThreads();
678
679 VBoxServiceVerbose(3, "Control: Disconnecting client ID=%u ...\n",
680 g_GuestControlSvcClientID);
681 VbglR3GuestCtrlDisconnect(g_GuestControlSvcClientID);
682 g_GuestControlSvcClientID = 0;
683
684 if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
685 {
686 RTSemEventMultiDestroy(g_hControlEvent);
687 g_hControlEvent = NIL_RTSEMEVENTMULTI;
688 }
689}
690
691
692/**
693 * Determines whether starting a new guest process according to the
694 * maximum number of concurrent guest processes defined is allowed or not.
695 *
696 * @return IPRT status code.
697 * @param pbAllowed True if starting (another) guest process
698 * is allowed, false if not.
699 */
700static int VBoxServiceControlStartAllowed(bool *pbAllowed)
701{
702 AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
703
704 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
705 if (RT_SUCCESS(rc))
706 {
707 /*
708 * Check if we're respecting our memory policy by checking
709 * how many guest processes are started and served already.
710 */
711 bool fLimitReached = false;
712 if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
713 {
714 uint32_t uProcsRunning = 0;
715 uint32_t uProcsStopped = 0;
716 PVBOXSERVICECTRLTHREAD pThread;
717 RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
718 {
719 VBOXSERVICECTRLTHREADSTATUS enmStatus = VBoxServiceControlThreadGetStatus(pThread);
720 if (enmStatus == VBOXSERVICECTRLTHREADSTATUS_STARTED)
721 uProcsRunning++;
722 else if (enmStatus == VBOXSERVICECTRLTHREADSTATUS_STOPPED)
723 uProcsStopped++;
724 else
725 AssertMsgFailed(("Control: Guest process neither started nor stopped!?\n"));
726 }
727
728 VBoxServiceVerbose(3, "Control: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
729 g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped);
730
731 int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1);
732 if (iProcsLeft < 0)
733 {
734 VBoxServiceVerbose(3, "Control: Maximum running guest processes reached (%u)\n",
735 g_GuestControlProcsMaxKept);
736 fLimitReached = true;
737 }
738 }
739
740 *pbAllowed = !fLimitReached;
741
742 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
743 if (RT_SUCCESS(rc))
744 rc = rc2;
745 }
746
747 return rc;
748}
749
750
751/**
752 * Finds a (formerly) started process given by its PID and locks it. Must be unlocked
753 * by the caller with VBoxServiceControlThreadUnlock().
754 *
755 * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL.
756 * @param uPID PID to search for.
757 */
758PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadLocked(uint32_t uPID)
759{
760 PVBOXSERVICECTRLTHREAD pThread = NULL;
761 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
762 if (RT_SUCCESS(rc))
763 {
764 PVBOXSERVICECTRLTHREAD pThreadCur;
765 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
766 {
767 if (pThreadCur->uPID == uPID)
768 {
769 rc = RTCritSectEnter(&pThreadCur->CritSect);
770 if (RT_SUCCESS(rc))
771 pThread = pThreadCur;
772 break;
773 }
774 }
775
776 int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
777 if (RT_SUCCESS(rc))
778 rc = rc2;
779 }
780
781 return pThread;
782}
783
784
785/**
786 * Unlocks a previously locked guest process thread.
787 *
788 * @param pThread Thread to unlock.
789 */
790void VBoxServiceControlThreadUnlock(const PVBOXSERVICECTRLTHREAD pThread)
791{
792 AssertPtr(pThread);
793
794 int rc = RTCritSectLeave(&pThread->CritSect);
795 AssertRC(rc);
796}
797
798
799/**
800 * Assigns a valid PID to a guest control thread and also checks if there already was
801 * another (stale) guest process which was using that PID before and destroys it.
802 *
803 * @return IPRT status code.
804 * @param pThread Thread to assign PID to.
805 * @param uPID PID to assign to the specified guest control execution thread.
806 */
807int VBoxServiceControlAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID)
808{
809 AssertPtrReturn(pThread, VERR_INVALID_POINTER);
810 AssertReturn(uPID, VERR_INVALID_PARAMETER);
811
812 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
813 if (RT_SUCCESS(rc))
814 {
815 /* Search old threads using the desired PID and shut them down completely -- it's
816 * not used anymore. */
817 PVBOXSERVICECTRLTHREAD pThreadCur;
818 bool fTryAgain = false;
819 do
820 {
821 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
822 {
823 if (pThreadCur->uPID == uPID)
824 {
825 Assert(pThreadCur != pThread); /* can't happen */
826 uint32_t uTriedPID = uPID;
827 uPID += 391939;
828 VBoxServiceVerbose(2, "ControlThread: PID %u was used before, trying again with %u ...\n",
829 uTriedPID, uPID);
830 fTryAgain = true;
831 break;
832 }
833 }
834 } while (fTryAgain);
835
836 /* Assign PID to current thread. */
837 pThread->uPID = uPID;
838
839 rc = RTCritSectLeave(&g_GuestControlThreadsCritSect);
840 AssertRC(rc);
841 }
842
843 return rc;
844}
845
846
847/**
848 * Removes the specified guest process thread from the global thread
849 * list.
850 *
851 * @return IPRT status code.
852 * @param pThread Thread to remove.
853 */
854void VBoxServiceControlRemoveThread(const PVBOXSERVICECTRLTHREAD pThread)
855{
856 if (!pThread)
857 return;
858
859 int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
860 if (RT_SUCCESS(rc))
861 {
862 VBoxServiceVerbose(4, "Control: Removing thread (PID: %u) from thread list\n",
863 pThread->uPID);
864 RTListNodeRemove(&pThread->Node);
865
866#ifdef DEBUG
867 PVBOXSERVICECTRLTHREAD pThreadCur;
868 uint32_t cThreads = 0;
869 RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
870 cThreads++;
871 VBoxServiceVerbose(4, "Control: Guest process threads left=%u\n", cThreads);
872#endif
873 rc = RTCritSectLeave(&g_GuestControlThreadsCritSect);
874 AssertRC(rc);
875 }
876}
877
878
879/**
880 * The 'vminfo' service description.
881 */
882VBOXSERVICE g_Control =
883{
884 /* pszName. */
885 "control",
886 /* pszDescription. */
887 "Host-driven Guest Control",
888 /* pszUsage. */
889 " [--control-interval <ms>] [--control-procs-max-kept <x>]\n"
890 " [--control-procs-mem-std[in|out|err] <KB>]"
891 ,
892 /* pszOptions. */
893 " --control-interval Specifies the interval at which to check for\n"
894 " new control commands. The default is 1000 ms.\n"
895 " --control-procs-max-kept\n"
896 " Specifies how many started guest processes are\n"
897 " kept into memory to work with. Default is 25.\n"
898 ,
899 /* methods */
900 VBoxServiceControlPreInit,
901 VBoxServiceControlOption,
902 VBoxServiceControlInit,
903 VBoxServiceControlWorker,
904 VBoxServiceControlStop,
905 VBoxServiceControlTerm
906};
907
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