VirtualBox

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

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

VBoxService/GuestCtrl: Adjusted guest process error reporting.

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