Changeset 42864 in vbox
- Timestamp:
- Aug 17, 2012 1:36:01 PM (12 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 4 deleted
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/Makefile.kmk
r42838 r42864 625 625 src-client/GuestSessionImpl.cpp \ 626 626 src-client/GuestCtrlImpl.cpp \ 627 src-client/GuestCtrlImplDir.cpp \628 src-client/GuestCtrlImplFile.cpp \629 627 src-client/KeyboardImpl.cpp \ 630 628 src-client/MachineDebuggerImpl.cpp \ … … 646 644 VBoxC_SOURCES += \ 647 645 src-client/GuestSessionImplTasks.cpp \ 648 src-client/GuestCtrlPrivate.cpp \ 649 src-client/GuestCtrlImplTasks.cpp \ 650 src-client/GuestDirEntryImpl.cpp 646 src-client/GuestCtrlPrivate.cpp 651 647 endif 652 648 … … 680 676 VBoxC_SOURCES += \ 681 677 src-client/EbmlWriter.cpp \ 682 src-client/VideoRec.cpp 678 src-client/VideoRec.cpp 683 679 endif 684 680 -
trunk/src/VBox/Main/idl/VirtualBox.xidl
r42846 r42864 8914 8914 8915 8915 <enum 8916 name="ExecuteProcessFlag"8917 uuid="1c49b831-b2c7-4a30-97dd-999a2e2cbf90"8918 >8919 <desc>8920 Guest process execution flags.8921 </desc>8922 8923 <const name="None" value="0">8924 <desc>No flag set.</desc>8925 </const>8926 <const name="WaitForProcessStartOnly" value="1">8927 <desc>Only use the specified timeout value to wait for starting the guest process - the guest8928 process itself then uses an infinite timeout.</desc>8929 </const>8930 <const name="IgnoreOrphanedProcesses" value="2">8931 <desc>Do not report an error when executed processes are still alive when VBoxService or the guest OS is shutting down.</desc>8932 </const>8933 <const name="Hidden" value="4">8934 <desc>Do not show the started process according to the guest OS guidelines.</desc>8935 </const>8936 <const name="NoProfile" value="8">8937 <desc>Do not use the user's profile data when exeuting a process. Only available for Windows guests.</desc>8938 </const>8939 <const name="WaitForStdOut" value="16">8940 <desc>The guest process waits until all data from stdout is read out.</desc>8941 </const>8942 <const name="WaitForStdErr" value="32">8943 <desc>The guest process waits until all data from stderr is read out.</desc>8944 </const>8945 </enum>8946 8947 <enum8948 name="ExecuteProcessStatus"8949 uuid="153768d9-d971-4098-8b5a-c5cb1ab9ea88"8950 >8951 <desc>8952 Guest process execution status.8953 </desc>8954 <const name="Undefined" value="0">8955 <desc>Process is in an undefined state.</desc>8956 </const>8957 8958 <const name="Started" value="1">8959 <desc>Process has been started.</desc>8960 </const>8961 <const name="TerminatedNormally" value="2">8962 <desc>Process terminated normally.</desc>8963 </const>8964 <const name="TerminatedSignal" value="3">8965 <desc>Process terminated via signal.</desc>8966 </const>8967 <const name="TerminatedAbnormally" value="4">8968 <desc>Process terminated abnormally.</desc>8969 </const>8970 <const name="TimedOutKilled" value="5">8971 <desc>Process timed out and was killed.</desc>8972 </const>8973 <const name="TimedOutAbnormally" value="6">8974 <desc>Process timed out and was not killed successfully.</desc>8975 </const>8976 <const name="Down" value="7">8977 <desc>Service/OS is stopping, process was killed.</desc>8978 </const>8979 <const name="Error" value="8">8980 <desc>Something went wrong (error code in flags).</desc>8981 </const>8982 </enum>8983 8984 <enum8985 8916 name="FileSeekType" 8986 8917 uuid="1b73f4f3-3515-4073-a506-76878d9e2541" … … 9406 9337 </enum> 9407 9338 9408 <enum9409 name="GuestDirEntryType"9410 uuid="6d19d924-1b77-4fc8-b369-a3b2c85c8241"9411 >9412 <desc>9413 Guest directory entry type.9414 </desc>9415 <const name="Unknown" value="0">9416 <desc>Unknown.</desc>9417 </const>9418 <const name="Directory" value="4">9419 <desc>Regular file.</desc>9420 </const>9421 <const name="File" value="10">9422 <desc>Regular file.</desc>9423 </const>9424 <const name="Symlink" value="12">9425 <desc>Symbolic link.</desc>9426 </const>9427 </enum>9428 9429 <interface9430 name="IGuestDirEntry" extends="$unknown"9431 uuid="20a66efc-c2f6-4438-826f-38454c04369e"9432 wsmap="struct"9433 >9434 <desc>9435 Structure representing a directory entry on the guest OS.9436 </desc>9437 <attribute name="nodeId" type="long long" readonly="yes">9438 <desc>The unique identifier (within the guest's file system) of this file system object.</desc>9439 </attribute>9440 <attribute name="name" type="wstring" readonly="yes">9441 <desc>The filename.</desc>9442 </attribute>9443 <attribute name="type" type="GuestDirEntryType" readonly="yes">9444 <desc>The entry type.</desc>9445 </attribute>9446 </interface>9447 9448 9339 <interface 9449 9340 name="IGuestSession" extends="$unknown" … … 11275 11166 </desc> 11276 11167 </param> 11277 </method> 11278 11279 <method name="executeProcess"> 11280 <desc> 11281 Executes an existing program inside the guest VM. 11282 11283 <note> 11284 Starting at VirtualBox 4.1.8 guest process execution by default is limited 11285 to serve up to 25 guest processes at a time. If all 25 guest processes 11286 are still active and running, starting a new guest process will result in an 11287 appropriate error message. 11288 11289 If ExecuteProcessFlag_WaitForStdOut and/or respectively 11290 ExecuteProcessFlag_WaitForStdErr of <link to="ExecuteProcessFlag"/> is 11291 set, the guest process will not exit until all data from the specified 11292 stream(s) is/are read out. 11293 11294 To raise or lower the guest process execution limit, either the guest property 11295 "/VirtualBox/GuestAdd/VBoxService/--control-procs-max-kept" or VBoxService' 11296 command line by specifying "--control-procs-max-kept" needs to be modified. 11297 A restart of the guest OS is required afterwards. To serve unlimited guest 11298 processes, a value of "0" needs to be set (not recommended). 11299 </note> 11300 11301 <result name="VBOX_E_IPRT_ERROR"> 11302 Could not execute process. 11303 </result> 11304 11305 </desc> 11306 <param name="execName" type="wstring" dir="in"> 11307 <desc> 11308 Full path name of the command to execute on the guest; the 11309 commands has to exists in the guest VM in order to be executed. 11310 </desc> 11311 </param> 11312 <param name="flags" type="unsigned long" dir="in"> 11313 <desc> 11314 <link to="ExecuteProcessFlag"/> flags. 11315 </desc> 11316 </param> 11317 <param name="arguments" type="wstring" safearray="yes" dir="in"> 11318 <desc> 11319 Array of arguments passed to the execution command. 11320 </desc> 11321 </param> 11322 <param name="environment" type="wstring" safearray="yes" dir="in"> 11323 <desc> 11324 Environment variables that can be set while the command is being 11325 executed, in form of "NAME=VALUE"; one pair per entry. To unset a 11326 variable just set its name ("NAME") without a value. 11327 </desc> 11328 </param> 11329 <param name="userName" type="wstring" dir="in"> 11330 <desc> 11331 User name under which the command will be executed; has to exist 11332 and have the appropriate rights to execute programs in the VM. 11333 </desc> 11334 </param> 11335 <param name="password" type="wstring" dir="in"> 11336 <desc> 11337 Password of the user account specified. 11338 </desc> 11339 </param> 11340 <param name="timeoutMS" type="unsigned long" dir="in"> 11341 <desc> 11342 The maximum timeout value (in msec) to wait for finished program 11343 execution. Pass 0 for an infinite timeout. 11344 </desc> 11345 </param> 11346 <param name="pid" type="unsigned long" dir="out"> 11347 <desc> 11348 The PID (process ID) of the started command for later reference. 11349 </desc> 11350 </param> 11351 <param name="progress" type="IProgress" dir="return"> 11352 <desc>Progress object to track the operation completion.</desc> 11353 </param> 11354 </method> 11355 11356 <method name="getProcessOutput"> 11357 <desc> 11358 Retrieves output of a formerly started and running guest process. 11359 11360 <note> 11361 Starting with VirtualBox 4.1.8 this only will return output data 11362 from stdout or stderr if flag ExecuteProcessFlag_WaitForStdOut 11363 and/or respectively ExecuteProcessFlag_WaitForStdErr of 11364 <link to="ExecuteProcessFlag"/> is set in the 11365 former <link to="#executeProcess"/> call for this guest process. 11366 </note> 11367 11368 <result name="VBOX_E_IPRT_ERROR"> 11369 Could not retrieve output. 11370 </result> 11371 11372 </desc> 11373 <param name="pid" type="unsigned long" dir="in"> 11374 <desc> 11375 Process id returned by earlier <link to="#executeProcess"/> call. 11376 </desc> 11377 </param> 11378 <param name="flags" type="unsigned long" dir="in"> 11379 <desc> 11380 <link to="ProcessOutputFlag"/> flags. 11381 </desc> 11382 </param> 11383 <param name="timeoutMS" type="unsigned long" dir="in"> 11384 <desc> 11385 The maximum timeout value (in msec) to wait for output 11386 data. Pass 0 for an infinite timeout. 11387 </desc> 11388 </param> 11389 <param name="size" type="long long" dir="in"> 11390 <desc> 11391 Size in bytes to read in the buffer. 11392 </desc> 11393 </param> 11394 <param name="data" type="octet" safearray="yes" dir="return"> 11395 <desc> 11396 Buffer for retrieving the actual output. A data size of 0 means end of file 11397 if the requested size was not 0. This is the unprocessed 11398 output data, i.e. the line ending style depends on the platform of 11399 the system the server is running on. 11400 </desc> 11401 </param> 11402 </method> 11403 11404 <method name="getProcessStatus"> 11405 <desc> 11406 Retrieves status, exit code and the exit reason of a formerly started 11407 guest process. If a guest process exited or got terminated this function 11408 returns its final status and removes this process from the list of 11409 known guest processes for further retrieval. 11410 11411 <result name="VBOX_E_IPRT_ERROR"> 11412 Process with specified PID was not found. 11413 </result> 11414 11415 </desc> 11416 <param name="pid" type="unsigned long" dir="in"> 11417 <desc> 11418 Process id returned by earlier <link to="#executeProcess"/> call. 11419 </desc> 11420 </param> 11421 <param name="exitcode" type="unsigned long" dir="out"> 11422 <desc> 11423 The exit code (if available). 11424 </desc> 11425 </param> 11426 <param name="flags" type="unsigned long" dir="out"> 11427 <desc> 11428 Additional flags of process status. Not used at the moment and 11429 must be set to 0. 11430 </desc> 11431 </param> 11432 <param name="reason" type="ExecuteProcessStatus" dir="return"> 11433 <desc> 11434 The current process status. 11435 </desc> 11436 </param> 11437 </method> 11438 11439 <method name="copyFromGuest"> 11440 <desc> 11441 Copies files/directories from guest to the host. 11442 11443 <result name="VBOX_E_IPRT_ERROR"> 11444 Error while copying. 11445 </result> 11446 11447 </desc> 11448 <param name="source" type="wstring" dir="in"> 11449 <desc> 11450 Source file on the guest to copy. 11451 </desc> 11452 </param> 11453 <param name="dest" type="wstring" dir="in"> 11454 <desc> 11455 Destination path on the host. 11456 </desc> 11457 </param> 11458 <param name="userName" type="wstring" dir="in"> 11459 <desc> 11460 User name under which the copy command will be executed; the 11461 user has to exist and have the appropriate rights to read from 11462 the source path. 11463 </desc> 11464 </param> 11465 <param name="password" type="wstring" dir="in"> 11466 <desc> 11467 Password of the user account specified. 11468 </desc> 11469 </param> 11470 <param name="flags" type="unsigned long" dir="in"> 11471 <desc> 11472 <link to="CopyFileFlag"/> flags. Not used at the moment and should be set to 0. 11473 </desc> 11474 </param> 11475 <param name="progress" type="IProgress" dir="return"> 11476 <desc>Progress object to track the operation completion.</desc> 11477 </param> 11478 </method> 11479 11480 <method name="copyToGuest"> 11481 <desc> 11482 Copies files/directories from host to the guest. 11483 11484 <result name="VBOX_E_IPRT_ERROR"> 11485 Error while copying. 11486 </result> 11487 11488 </desc> 11489 <param name="source" type="wstring" dir="in"> 11490 <desc> 11491 Source file on the host to copy. 11492 </desc> 11493 </param> 11494 <param name="dest" type="wstring" dir="in"> 11495 <desc> 11496 Destination path on the guest. 11497 </desc> 11498 </param> 11499 <param name="userName" type="wstring" dir="in"> 11500 <desc> 11501 User name under which the copy command will be executed; the 11502 user has to exist and have the appropriate rights to write to 11503 the destination path. 11504 </desc> 11505 </param> 11506 <param name="password" type="wstring" dir="in"> 11507 <desc> 11508 Password of the user account specified. 11509 </desc> 11510 </param> 11511 <param name="flags" type="unsigned long" dir="in"> 11512 <desc> 11513 <link to="CopyFileFlag"/> flags. Not used at the moment and should be set to 0. 11514 </desc> 11515 </param> 11516 <param name="progress" type="IProgress" dir="return"> 11517 <desc>Progress object to track the operation completion.</desc> 11518 </param> 11519 </method> 11520 11521 <method name="directoryClose"> 11522 <desc> 11523 Closes a formerly opened guest directory. 11524 11525 <result name="VBOX_E_IPRT_ERROR"> 11526 Error while closing directory. 11527 </result> 11528 11529 </desc> 11530 <param name="handle" type="unsigned long" dir="in"> 11531 <desc> 11532 Handle of opened directory to close. 11533 </desc> 11534 </param> 11535 </method> 11536 11537 <method name="directoryCreate"> 11538 <desc> 11539 Creates a directory on the guest. 11540 11541 <result name="VBOX_E_IPRT_ERROR"> 11542 Error while creating directory. 11543 </result> 11544 11545 </desc> 11546 <param name="directory" type="wstring" dir="in"> 11547 <desc> 11548 Directory to create. 11549 </desc> 11550 </param> 11551 <param name="userName" type="wstring" dir="in"> 11552 <desc> 11553 User name under which the directory creation will be executed; the 11554 user has to exist and have the appropriate rights to create the 11555 desired directory. 11556 </desc> 11557 </param> 11558 <param name="password" type="wstring" dir="in"> 11559 <desc> 11560 Password of the user account specified. 11561 </desc> 11562 </param> 11563 <param name="mode" type="unsigned long" dir="in"> 11564 <desc> 11565 File mode. 11566 </desc> 11567 </param> 11568 <param name="flags" type="unsigned long" dir="in"> 11569 <desc> 11570 <link to="DirectoryCreateFlag"/> flags. 11571 </desc> 11572 </param> 11573 </method> 11574 11575 <method name="directoryOpen"> 11576 <desc> 11577 Opens a directory on the guest. 11578 11579 <result name="VBOX_E_IPRT_ERROR"> 11580 Error while opening / reading directory. 11581 </result> 11582 11583 </desc> 11584 <param name="directory" type="wstring" dir="in"> 11585 <desc> 11586 Directory to read. 11587 </desc> 11588 </param> 11589 <param name="filter" type="wstring" dir="in"> 11590 <desc> 11591 Directory filter (DOS style wildcards). Set to empty 11592 string if no filter required. 11593 </desc> 11594 </param> 11595 <param name="flags" type="unsigned long" dir="in"> 11596 <desc> 11597 <link to="DirectoryOpenFlag"/> flags. 11598 </desc> 11599 </param> 11600 <param name="userName" type="wstring" dir="in"> 11601 <desc> 11602 User name under which the directory reading will be performed; the 11603 user has to exist and have the appropriate rights to access / read the 11604 desired directory. 11605 </desc> 11606 </param> 11607 <param name="password" type="wstring" dir="in"> 11608 <desc> 11609 Password of the user account specified. 11610 </desc> 11611 </param> 11612 <param name="handle" type="unsigned long" dir="return"> 11613 <desc> 11614 Handle of opened directory returned by openDirectory. 11615 </desc> 11616 </param> 11617 </method> 11618 11619 <method name="directoryRead"> 11620 <desc> 11621 Reads the next directory entry of an opened guest directory. 11622 11623 <result name="E_ABORT"> 11624 When the end of the directory has been reached. 11625 </result> 11626 11627 <result name="VBOX_E_IPRT_ERROR"> 11628 Error while opening / reading directory. 11629 </result> 11630 11631 </desc> 11632 <param name="handle" type="unsigned long" dir="in"> 11633 <desc> 11634 Handle of opened directory returned by openDirectory. 11635 </desc> 11636 </param> 11637 <param name="entry" type="IGuestDirEntry" dir="return"> 11638 <desc> 11639 Information about next directory entry on success. 11640 </desc> 11641 </param> 11642 </method> 11643 11644 <method name="fileExists"> 11645 <desc> 11646 Checks if the specified file name exists and is a regular file. 11647 11648 If the file name ends with a slash or backslash, the function assumes 11649 it's a directory and will check if the specified directory exists and 11650 is a regular directory. 11651 11652 <result name="VBOX_E_IPRT_ERROR"> 11653 Error while looking up information. 11654 </result> 11655 11656 </desc> 11657 <param name="file" type="wstring" dir="in"> 11658 <desc> 11659 Full path of file to check. 11660 </desc> 11661 </param> 11662 <param name="userName" type="wstring" dir="in"> 11663 <desc> 11664 User name under which the lookup will be performed; the 11665 user has to exist and have the appropriate rights to access / read the 11666 desired directory. 11667 </desc> 11668 </param> 11669 <param name="password" type="wstring" dir="in"> 11670 <desc> 11671 Password of the user account specified. 11672 </desc> 11673 </param> 11674 <param name="exists" type="boolean" dir="return"> 11675 <desc> 11676 True if it's a regular file, false if it isn't (or doesn't exist). 11677 </desc> 11678 </param> 11679 </method> 11680 11681 <method name="fileQuerySize"> 11682 <desc> 11683 Queries the size of a file, given the path to it. 11684 11685 <result name="VBOX_E_IPRT_ERROR"> 11686 Error while looking up information. 11687 </result> 11688 11689 </desc> 11690 <param name="file" type="wstring" dir="in"> 11691 <desc> 11692 Full path of file to query file size for. 11693 </desc> 11694 </param> 11695 <param name="userName" type="wstring" dir="in"> 11696 <desc> 11697 User name under which the lookup will be performed; the 11698 user has to exist and have the appropriate rights to access / read the 11699 desired directory. 11700 </desc> 11701 </param> 11702 <param name="password" type="wstring" dir="in"> 11703 <desc> 11704 Password of the user account specified. 11705 </desc> 11706 </param> 11707 <param name="size" type="long long" dir="return"> 11708 <desc> 11709 Size (in bytes) of file specified. 11710 </desc> 11711 </param> 11712 </method> 11713 11714 <method name="setProcessInput"> 11715 <desc> 11716 Sends input into a formerly started process. 11717 11718 <result name="VBOX_E_IPRT_ERROR"> 11719 Could not send input. 11720 </result> 11721 11722 </desc> 11723 <param name="pid" type="unsigned long" dir="in"> 11724 <desc> 11725 Process id returned by earlier <link to="#executeProcess"/> call. 11726 </desc> 11727 </param> 11728 <param name="flags" type="unsigned long" dir="in"> 11729 <desc> 11730 <link to="ProcessInputFlag"/> flags. 11731 </desc> 11732 </param> 11733 <param name="timeoutMS" type="unsigned long" dir="in"> 11734 <desc> 11735 The maximum timeout value (in msec) to wait for getting the 11736 data transfered to the guest. Pass 0 for an infinite timeout. 11737 </desc> 11738 </param> 11739 <param name="data" type="octet" dir="in" safearray="yes"> 11740 <desc> 11741 Buffer of input data to send to the started process to. 11742 </desc> 11743 </param> 11744 <param name="written" type="unsigned long" dir="return"> 11745 <desc> 11746 Number of bytes written. 11747 </desc> 11748 </param> 11749 </method> 11168 </method> 11750 11169 11751 11170 <method name="updateGuestAdditions"> -
trunk/src/VBox/Main/include/GuestImpl.h
r42693 r42864 27 27 #include "GuestSessionImpl.h" 28 28 #include "HGCM.h" 29 #ifdef VBOX_WITH_GUEST_CONTROL30 # include <iprt/fs.h>31 # include <VBox/HostServices/GuestControlSvc.h>32 using namespace guestControl;33 #endif34 29 35 30 #ifdef VBOX_WITH_DRAG_AND_DROP … … 73 68 DECLARE_EMPTY_CTOR_DTOR (Guest) 74 69 75 HRESULT FinalConstruct( );76 void FinalRelease( );77 78 // Public initializer/uninitializer for internal purposes only 70 HRESULT FinalConstruct(void); 71 void FinalRelease(void); 72 73 // Public initializer/uninitializer for internal purposes only. 79 74 HRESULT init (Console *aParent); 80 75 void uninit(); 81 76 82 // IGuest properties 77 // IGuest properties. 83 78 STDMETHOD(COMGETTER(OSTypeId)) (BSTR *aOSTypeId); 84 79 STDMETHOD(COMGETTER(AdditionsRunLevel)) (AdditionsRunLevelType_T *aRunLevel); … … 91 86 STDMETHOD(COMGETTER(StatisticsUpdateInterval)) (ULONG *aUpdateInterval); 92 87 STDMETHOD(COMSETTER(StatisticsUpdateInterval)) (ULONG aUpdateInterval); 93 94 // IGuest methods 88 // IGuest methods. 95 89 STDMETHOD(GetFacilityStatus)(AdditionsFacilityType_T aType, LONG64 *aTimestamp, AdditionsFacilityStatus_T *aStatus); 96 90 STDMETHOD(GetAdditionsStatus)(AdditionsRunLevelType_T aLevel, BOOL *aActive); 97 91 STDMETHOD(SetCredentials)(IN_BSTR aUsername, IN_BSTR aPassword, 98 92 IN_BSTR aDomain, BOOL aAllowInteractiveLogon); 93 // Drag'n drop support. 99 94 STDMETHOD(DragHGEnter)(ULONG uScreenId, ULONG uX, ULONG uY, DragAndDropAction_T defaultAction, ComSafeArrayIn(DragAndDropAction_T, allowedActions), ComSafeArrayIn(IN_BSTR, formats), DragAndDropAction_T *pResultAction); 100 95 STDMETHOD(DragHGMove)(ULONG uScreenId, ULONG uX, ULONG uY, DragAndDropAction_T defaultAction, ComSafeArrayIn(DragAndDropAction_T, allowedActions), ComSafeArrayIn(IN_BSTR, formats), DragAndDropAction_T *pResultAction); … … 105 100 STDMETHOD(DragGHDropped)(IN_BSTR strFormat, DragAndDropAction_T action, IProgress **ppProgress); 106 101 STDMETHOD(DragGHGetData)(ComSafeArrayOut(BYTE, data)); 107 // Process execution108 STDMETHOD(ExecuteProcess)(IN_BSTR aCommand, ULONG aFlags,109 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),110 IN_BSTR aUsername, IN_BSTR aPassword,111 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress);112 STDMETHOD(GetProcessOutput)(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData));113 STDMETHOD(SetProcessInput)(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten);114 STDMETHOD(GetProcessStatus)(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ExecuteProcessStatus_T *aStatus);115 // File copying116 STDMETHOD(CopyFromGuest)(IN_BSTR aSource, IN_BSTR aDest, IN_BSTR aUsername, IN_BSTR aPassword, ULONG aFlags, IProgress **aProgress);117 STDMETHOD(CopyToGuest)(IN_BSTR aSource, IN_BSTR aDest, IN_BSTR aUsername, IN_BSTR aPassword, ULONG aFlags, IProgress **aProgress);118 // Directory handling119 STDMETHOD(DirectoryClose)(ULONG aHandle);120 STDMETHOD(DirectoryCreate)(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, ULONG aMode, ULONG aFlags);121 #if 0122 STDMETHOD(DirectoryExists)(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists);123 #endif124 STDMETHOD(DirectoryOpen)(IN_BSTR aDirectory, IN_BSTR aFilter,125 ULONG aFlags, IN_BSTR aUsername, IN_BSTR aPassword, ULONG *aHandle);126 STDMETHOD(DirectoryRead)(ULONG aHandle, IGuestDirEntry **aDirEntry);127 // File handling128 STDMETHOD(FileExists)(IN_BSTR aFile, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists);129 STDMETHOD(FileQuerySize)(IN_BSTR aFile, IN_BSTR aUsername, IN_BSTR aPassword, LONG64 *aSize);130 102 // Misc stuff 131 103 STDMETHOD(InternalGetStatistics)(ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle, … … 136 108 STDMETHOD(FindSession)(IN_BSTR aSessionName, ComSafeArrayOut(IGuestSession *, aSessions)); 137 109 138 // Public methods that are not in IDL (only called internally). 110 public: 111 /** @name Static internal methods. 112 * @{ */ 113 #ifdef VBOX_WITH_GUEST_CONTROL 114 /** Static callback for handling guest control notifications. */ 115 static DECLCALLBACK(int) notifyCtrlDispatcher(void *pvExtension, uint32_t u32Function, void *pvParms, uint32_t cbParms); 116 static void staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick); 117 #endif 118 /** @} */ 119 120 public: 121 /** @name Public internal methods. 122 * @{ */ 123 void enableVMMStatistics(BOOL aEnable) { mCollectVMMStats = aEnable; }; 139 124 void setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType); 140 125 void setAdditionsInfo2(uint32_t a_uFullVersion, const char *a_pszName, uint32_t a_uRevision, uint32_t a_fFeatures); … … 150 135 return setErrorInternal(aResultCode, getStaticClassIID(), getStaticComponentName(), aText, false, true); 151 136 } 152 153 # ifdef VBOX_WITH_GUEST_CONTROL 154 // Internal guest directory functions 155 int directoryCreateHandle(ULONG *puHandle, ULONG uPID, IN_BSTR aDirectory, IN_BSTR aFilter, ULONG uFlags); 156 HRESULT directoryCreateInternal(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, 157 ULONG aMode, ULONG aFlags, int *pRC); 158 void directoryDestroyHandle(uint32_t uHandle); 159 HRESULT directoryExistsInternal(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists); 160 uint32_t directoryGetPID(uint32_t uHandle); 161 int directoryGetNextEntry(uint32_t uHandle, GuestProcessStreamBlock &streamBlock); 162 bool directoryHandleExists(uint32_t uHandle); 163 HRESULT directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter, 164 ULONG aFlags, 165 IN_BSTR aUsername, IN_BSTR aPassword, 166 ULONG *aHandle, int *pRC); 167 HRESULT directoryQueryInfoInternal(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, PRTFSOBJINFO aObjInfo, RTFSOBJATTRADD enmAddAttribs, int *pRC); 168 // Internal guest execution functions 169 HRESULT executeAndWaitForTool(IN_BSTR aTool, IN_BSTR aDescription, 170 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment), 171 IN_BSTR aUsername, IN_BSTR aPassword, 172 ULONG uFlagsToAdd, 173 GuestCtrlStreamObjects *pObjStdOut, GuestCtrlStreamObjects *pObjStdErr, 174 IProgress **aProgress, ULONG *aPID); 175 HRESULT executeProcessInternal(IN_BSTR aCommand, ULONG aFlags, 176 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment), 177 IN_BSTR aUsername, IN_BSTR aPassword, 178 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC); 179 HRESULT getProcessOutputInternal(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, 180 LONG64 aSize, ComSafeArrayOut(BYTE, aData), int *pRC); 181 HRESULT executeSetResult(const char *pszCommand, const char *pszUser, ULONG ulTimeout, PCALLBACKDATAEXECSTATUS pExecStatus, ULONG *puPID); 182 int executeStreamQueryFsObjInfo(IN_BSTR aObjName,GuestProcessStreamBlock &streamBlock, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttribs); 183 int executeStreamDrain(ULONG aPID, ULONG ulFlags, GuestProcessStream *pStream); 184 int executeStreamGetNextBlock(ULONG ulPID, ULONG ulFlags, GuestProcessStream &stream, GuestProcessStreamBlock &streamBlock); 185 int executeStreamParseNextBlock(ULONG ulPID, ULONG ulFlags, GuestProcessStream &stream, GuestProcessStreamBlock &streamBlock); 186 HRESULT executeStreamParse(ULONG uPID, ULONG ulFlags, GuestCtrlStreamObjects &streamObjects); 187 HRESULT executeWaitForExit(ULONG uPID, ComPtr<IProgress> pProgress, ULONG uTimeoutMS); 188 // Internal guest file functions 189 HRESULT fileExistsInternal(IN_BSTR aFile, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists); 190 HRESULT fileQueryInfoInternal(IN_BSTR aFile, IN_BSTR aUsername, IN_BSTR aPassword, PRTFSOBJINFO aObjInfo, RTFSOBJATTRADD enmAddAttribs, int *pRC); 191 HRESULT fileQuerySizeInternal(IN_BSTR aFile, IN_BSTR aUsername, IN_BSTR aPassword, LONG64 *aSize); 192 193 // Guest control dispatcher. 194 /** Static callback for handling guest control notifications. */ 195 static DECLCALLBACK(int) notifyCtrlDispatcher(void *pvExtension, uint32_t u32Function, void *pvParms, uint32_t cbParms); 196 197 // Internal tasks. 198 //extern struct GuestTask; 199 HRESULT taskCopyFileToGuest(GuestTask *aTask); 200 HRESULT taskCopyFileFromGuest(GuestTask *aTask); 201 HRESULT taskUpdateGuestAdditions(GuestTask *aTask); 202 #endif 203 void enableVMMStatistics(BOOL aEnable) { mCollectVMMStats = aEnable; }; 204 205 public: 206 /** @name Public internal methods. 207 * @{ */ 137 #ifdef VBOX_WITH_GUEST_CONTROL 208 138 int dispatchToSession(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData); 209 139 uint32_t getAdditionsVersion(void) { return mData.mAdditionsVersionFull; } … … 213 143 const Utf8Str &strSessionName, ComObjPtr<GuestSession> &pGuestSession); 214 144 inline bool sessionExists(uint32_t uSessionID); 145 #endif 215 146 /** @} */ 216 147 217 148 private: 218 219 #ifdef VBOX_WITH_GUEST_CONTROL 220 // Internal guest callback representation. 221 typedef struct VBOXGUESTCTRL_CALLBACK 222 { 223 eVBoxGuestCtrlCallbackType mType; 224 /** Pointer to user-supplied data. */ 225 void *pvData; 226 /** Size of user-supplied data. */ 227 uint32_t cbData; 228 /** The host PID. Needed for translating to 229 * a guest PID. */ 230 uint32_t uHostPID; 231 /** Pointer to user-supplied IProgress. */ 232 ComObjPtr<Progress> pProgress; 233 } VBOXGUESTCTRL_CALLBACK, *PVBOXGUESTCTRL_CALLBACK; 234 typedef std::map< uint32_t, VBOXGUESTCTRL_CALLBACK > CallbackMap; 235 typedef std::map< uint32_t, VBOXGUESTCTRL_CALLBACK >::iterator CallbackMapIter; 236 typedef std::map< uint32_t, VBOXGUESTCTRL_CALLBACK >::const_iterator CallbackMapIterConst; 237 238 int callbackAdd(const PVBOXGUESTCTRL_CALLBACK pCallbackData, uint32_t *puContextID); 239 int callbackAssignHostPID(uint32_t uContextID, uint32_t uHostPID); 240 void callbackDestroy(uint32_t uContextID); 241 void callbackRemove(uint32_t uContextID); 242 bool callbackExists(uint32_t uContextID); 243 void callbackFreeUserData(void *pvData); 244 uint32_t callbackGetHostPID(uint32_t uContextID); 245 int callbackGetUserData(uint32_t uContextID, eVBoxGuestCtrlCallbackType *pEnmType, void **ppvData, size_t *pcbData); 246 void* callbackGetUserDataMutableRaw(uint32_t uContextID, size_t *pcbData); 247 int callbackInit(PVBOXGUESTCTRL_CALLBACK pCallback, eVBoxGuestCtrlCallbackType enmType, ComPtr<Progress> pProgress); 248 bool callbackIsCanceled(uint32_t uContextID); 249 bool callbackIsComplete(uint32_t uContextID); 250 int callbackMoveForward(uint32_t uContextID, const char *pszMessage); 251 int callbackNotifyEx(uint32_t uContextID, int iRC, const char *pszMessage); 252 int callbackNotifyComplete(uint32_t uContextID); 253 int callbackNotifyAllForPID(uint32_t uGuestPID, int iRC, const char *pszMessage); 254 int callbackWaitForCompletion(uint32_t uContextID, LONG lStage, LONG lTimeout); 255 256 int notifyCtrlClientDisconnected(uint32_t u32Function, PCALLBACKDATACLIENTDISCONNECTED pData); 257 int notifyCtrlExecStatus(uint32_t u32Function, PCALLBACKDATAEXECSTATUS pData); 258 int notifyCtrlExecOut(uint32_t u32Function, PCALLBACKDATAEXECOUT pData); 259 int notifyCtrlExecInStatus(uint32_t u32Function, PCALLBACKDATAEXECINSTATUS pData); 260 261 // Internal guest process representation. 262 typedef struct VBOXGUESTCTRL_PROCESS 263 { 264 /** The guest PID -- needed for controlling the actual guest 265 * process which has its own PID (generated by the guest OS). */ 266 uint32_t mGuestPID; 267 /** The last reported process status. */ 268 ExecuteProcessStatus_T mStatus; 269 /** The process execution flags. */ 270 uint32_t mFlags; 271 /** The process' exit code. */ 272 uint32_t mExitCode; 273 } VBOXGUESTCTRL_PROCESS, *PVBOXGUESTCTRL_PROCESS; 274 typedef std::map< uint32_t, VBOXGUESTCTRL_PROCESS > GuestProcessMap; 275 typedef std::map< uint32_t, VBOXGUESTCTRL_PROCESS >::iterator GuestProcessMapIter; 276 typedef std::map< uint32_t, VBOXGUESTCTRL_PROCESS >::const_iterator GuestProcessMapIterConst; 277 278 uint32_t processGetGuestPID(uint32_t uHostPID); 279 int processGetStatus(uint32_t uHostPID, PVBOXGUESTCTRL_PROCESS pProcess, bool fRemove); 280 int processSetStatus(uint32_t uHostPID, uint32_t uGuestPID, 281 ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags); 282 283 // Internal guest directory representation. 284 typedef struct VBOXGUESTCTRL_DIRECTORY 285 { 286 Bstr mDirectory; 287 Bstr mFilter; 288 ULONG mFlags; 289 /** Associated host PID of started vbox_ls tool. */ 290 ULONG mPID; 291 GuestProcessStream mStream; 292 } VBOXGUESTCTRL_DIRECTORY, *PVBOXGUESTCTRL_DIRECTORY; 293 typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY > GuestDirectoryMap; 294 typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY >::iterator GuestDirectoryMapIter; 295 typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY >::const_iterator GuestDirectoryMapIterConst; 296 297 // Utility functions. 298 int prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv); 299 300 /* 301 * Handler for guest execution control notifications. 302 */ 303 HRESULT setErrorCompletion(int rc); 304 HRESULT setErrorFromProgress(ComPtr<IProgress> pProgress); 305 HRESULT setErrorHGCM(int rc); 306 # endif 149 /** @name Private internal methods. 150 * @{ */ 151 void updateStats(uint64_t iTick); 152 /** @} */ 307 153 308 154 typedef std::map< AdditionsFacilityType_T, ComObjPtr<AdditionsFacility> > FacilityMap; … … 331 177 }; 332 178 333 ULONG mMemoryBalloonSize;334 ULONG mStatUpdateInterval;335 ULONG mCurrentGuestStat[GUESTSTATTYPE_MAX];336 ULONG mGuestValidStats;337 BOOL mCollectVMMStats;338 BOOL mfPageFusionEnabled;179 ULONG mMemoryBalloonSize; 180 ULONG mStatUpdateInterval; 181 ULONG mCurrentGuestStat[GUESTSTATTYPE_MAX]; 182 ULONG mGuestValidStats; 183 BOOL mCollectVMMStats; 184 BOOL mfPageFusionEnabled; 339 185 340 186 Console *mParent; 341 187 Data mData; 342 188 343 # 189 #ifdef VBOX_WITH_GUEST_CONTROL 344 190 /** General extension callback for guest control. */ 345 191 HGCMSVCEXTHANDLE mhExtCtrl; 346 /** Next upcoming context ID. */ 347 volatile uint32_t mNextContextID; 348 /** Next upcoming host PID */ 349 volatile uint32_t mNextHostPID; 350 /** Next upcoming directory handle ID. */ 351 volatile uint32_t mNextDirectoryID; 352 CallbackMap mCallbackMap; 353 GuestDirectoryMap mGuestDirectoryMap; 354 GuestProcessMap mGuestProcessMap; 355 # endif 192 #endif 356 193 357 194 #ifdef VBOX_WITH_DRAG_AND_DROP … … 360 197 friend class GuestDnDPrivate; 361 198 #endif 362 static void staticUpdateStats(RTTIMERLR hTimerLR, void *pvUser, uint64_t iTick); 363 void updateStats(uint64_t iTick); 199 364 200 RTTIMERLR mStatTimer; 365 201 uint32_t mMagic; -
trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp
r42758 r42864 47 47 #ifdef VBOX_WITH_GUEST_CONTROL 48 48 /** 49 * Appends environment variables to the environment block.50 *51 * Each var=value pair is separated by the null character ('\\0'). The whole52 * block will be stored in one blob and disassembled on the guest side later to53 * fit into the HGCM param structure.54 *55 * @returns VBox status code.56 *57 * @param pszEnvVar The environment variable=value to append to the58 * environment block.59 * @param ppvList This is actually a pointer to a char pointer60 * variable which keeps track of the environment block61 * that we're constructing.62 * @param pcbList Pointer to the variable holding the current size of63 * the environment block. (List is a misnomer, go64 * ahead a be confused.)65 * @param pcEnvVars Pointer to the variable holding count of variables66 * stored in the environment block.67 */68 int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnvVars)69 {70 int rc = VINF_SUCCESS;71 uint32_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);72 if (*ppvList)73 {74 uint32_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */75 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);76 if (pvTmp == NULL)77 rc = VERR_NO_MEMORY;78 else79 {80 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);81 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */82 *ppvList = (void **)pvTmp;83 }84 }85 else86 {87 char *pszTmp;88 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)89 {90 *ppvList = (void **)pszTmp;91 /* Reset counters. */92 *pcEnvVars = 0;93 *pcbList = 0;94 }95 }96 if (RT_SUCCESS(rc))97 {98 *pcbList += cchEnv + 1; /* Include zero termination. */99 *pcEnvVars += 1; /* Increase env variable count. */100 }101 return rc;102 }103 104 /**105 * Adds a callback with a user provided data block and an optional progress object106 * to the callback map. A callback is identified by a unique context ID which is used107 * to identify a callback from the guest side.108 *109 * @return IPRT status code.110 * @param pCallback111 * @param puContextID112 */113 int Guest::callbackAdd(const PVBOXGUESTCTRL_CALLBACK pCallback, uint32_t *puContextID)114 {115 AssertPtrReturn(pCallback, VERR_INVALID_PARAMETER);116 /* puContextID is optional. */117 118 int rc = VERR_NOT_FOUND;119 120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);121 122 /* Create a new context ID and assign it. */123 uint32_t uNewContextID = 0;124 uint32_t uTries = 0;125 for (;;)126 {127 /* Create a new context ID ... */128 uNewContextID = ASMAtomicIncU32(&mNextContextID);129 if (uNewContextID == UINT32_MAX)130 ASMAtomicUoWriteU32(&mNextContextID, 1000);131 /* Is the context ID already used? Try next ID ... */132 if (!callbackExists(uNewContextID))133 {134 /* Callback with context ID was not found. This means135 * we can use this context ID for our new callback we want136 * to add below. */137 rc = VINF_SUCCESS;138 break;139 }140 141 if (++uTries == UINT32_MAX)142 break; /* Don't try too hard. */143 }144 145 if (RT_SUCCESS(rc))146 {147 /* Add callback with new context ID to our callback map. */148 mCallbackMap[uNewContextID] = *pCallback;149 Assert(mCallbackMap.size());150 151 /* Report back new context ID. */152 if (puContextID)153 *puContextID = uNewContextID;154 }155 156 return rc;157 }158 159 /**160 * Assigns a host PID to a specified context.161 * Does not do locking!162 *163 * @param uContextID164 * @param uHostPID165 */166 int Guest::callbackAssignHostPID(uint32_t uContextID, uint32_t uHostPID)167 {168 AssertReturn(uContextID, VERR_INVALID_PARAMETER);169 AssertReturn(uHostPID, VERR_INVALID_PARAMETER);170 171 int rc = VINF_SUCCESS;172 173 CallbackMapIter it = mCallbackMap.find(uContextID);174 if (it == mCallbackMap.end())175 return VERR_NOT_FOUND;176 177 it->second.uHostPID = uHostPID;178 179 return VINF_SUCCESS;180 }181 182 /**183 * Destroys the formerly allocated callback data. The callback then184 * needs to get removed from the callback map via callbackRemove().185 * Does not do locking!186 *187 * @param uContextID188 */189 void Guest::callbackDestroy(uint32_t uContextID)190 {191 AssertReturnVoid(uContextID);192 193 CallbackMapIter it = mCallbackMap.find(uContextID);194 if (it != mCallbackMap.end())195 {196 LogFlowFunc(("Callback with CID=%u found\n", uContextID));197 if (it->second.pvData)198 {199 LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));200 201 callbackFreeUserData(it->second.pvData);202 it->second.cbData = 0;203 }204 }205 }206 207 /**208 * Removes a callback from the callback map.209 * Does not do locking!210 *211 * @param uContextID212 */213 void Guest::callbackRemove(uint32_t uContextID)214 {215 callbackDestroy(uContextID);216 217 mCallbackMap.erase(uContextID);218 }219 220 /**221 * Checks whether a callback with the given context ID222 * exists or not.223 * Does not do locking!224 *225 * @return bool True, if callback exists, false if not.226 * @param uContextID Context ID to check.227 */228 bool Guest::callbackExists(uint32_t uContextID)229 {230 AssertReturn(uContextID, false);231 232 CallbackMapIter it = mCallbackMap.find(uContextID);233 return (it == mCallbackMap.end()) ? false : true;234 }235 236 /**237 * Frees the user data (actual context data) of a callback.238 * Does not do locking!239 *240 * @param pvData Data to free.241 */242 void Guest::callbackFreeUserData(void *pvData)243 {244 if (pvData)245 {246 RTMemFree(pvData);247 pvData = NULL;248 }249 }250 251 /**252 * Retrieves the (generated) host PID of a given callback.253 *254 * @return The host PID, if found, 0 otherwise.255 * @param uContextID Context ID to lookup host PID for.256 * @param puHostPID Where to store the host PID.257 */258 uint32_t Guest::callbackGetHostPID(uint32_t uContextID)259 {260 AssertReturn(uContextID, 0);261 262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);263 264 CallbackMapIterConst it = mCallbackMap.find(uContextID);265 if (it == mCallbackMap.end())266 return 0;267 268 return it->second.uHostPID;269 }270 271 int Guest::callbackGetUserData(uint32_t uContextID, eVBoxGuestCtrlCallbackType *pEnmType,272 void **ppvData, size_t *pcbData)273 {274 AssertReturn(uContextID, VERR_INVALID_PARAMETER);275 /* pEnmType is optional. */276 /* ppvData is optional. */277 /* pcbData is optional. */278 279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);280 281 CallbackMapIterConst it = mCallbackMap.find(uContextID);282 if (it == mCallbackMap.end())283 return VERR_NOT_FOUND;284 285 if (pEnmType)286 *pEnmType = it->second.mType;287 288 if ( ppvData289 && it->second.cbData)290 {291 void *pvData = RTMemAlloc(it->second.cbData);292 AssertPtrReturn(pvData, VERR_NO_MEMORY);293 memcpy(pvData, it->second.pvData, it->second.cbData);294 *ppvData = pvData;295 }296 297 if (pcbData)298 *pcbData = it->second.cbData;299 300 return VINF_SUCCESS;301 }302 303 /* Does not do locking! Caller has to take care of it because the caller needs to304 * modify the data ...*/305 void* Guest::callbackGetUserDataMutableRaw(uint32_t uContextID, size_t *pcbData)306 {307 /* uContextID can be 0. */308 /* pcbData is optional. */309 310 CallbackMapIterConst it = mCallbackMap.find(uContextID);311 if (it != mCallbackMap.end())312 {313 if (pcbData)314 *pcbData = it->second.cbData;315 return it->second.pvData;316 }317 318 return NULL;319 }320 321 int Guest::callbackInit(PVBOXGUESTCTRL_CALLBACK pCallback, eVBoxGuestCtrlCallbackType enmType,322 ComPtr<Progress> pProgress)323 {324 AssertPtrReturn(pCallback, VERR_INVALID_POINTER);325 /* Everything else is optional. */326 327 int vrc = VINF_SUCCESS;328 switch (enmType)329 {330 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:331 {332 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));333 AssertPtrReturn(pData, VERR_NO_MEMORY);334 RT_BZERO(pData, sizeof(CALLBACKDATAEXECSTATUS));335 pCallback->cbData = sizeof(CALLBACKDATAEXECSTATUS);336 pCallback->pvData = pData;337 break;338 }339 340 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:341 {342 PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));343 AssertPtrReturn(pData, VERR_NO_MEMORY);344 RT_BZERO(pData, sizeof(CALLBACKDATAEXECOUT));345 pCallback->cbData = sizeof(CALLBACKDATAEXECOUT);346 pCallback->pvData = pData;347 break;348 }349 350 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:351 {352 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));353 AssertPtrReturn(pData, VERR_NO_MEMORY);354 RT_BZERO(pData, sizeof(CALLBACKDATAEXECINSTATUS));355 pCallback->cbData = sizeof(CALLBACKDATAEXECINSTATUS);356 pCallback->pvData = pData;357 break;358 }359 360 default:361 vrc = VERR_INVALID_PARAMETER;362 break;363 }364 365 if (RT_SUCCESS(vrc))366 {367 /* Init/set common stuff. */368 pCallback->mType = enmType;369 pCallback->pProgress = pProgress;370 }371 372 return vrc;373 }374 375 bool Guest::callbackIsCanceled(uint32_t uContextID)376 {377 if (!uContextID)378 return true; /* If no context ID given then take a shortcut. */379 380 ComPtr<IProgress> pProgress;381 {382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);383 384 CallbackMapIterConst it = mCallbackMap.find(uContextID);385 if (it != mCallbackMap.end())386 pProgress = it->second.pProgress;387 }388 389 if (pProgress)390 {391 BOOL fCanceled = FALSE;392 HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);393 if ( SUCCEEDED(hRC)394 && !fCanceled)395 {396 return false;397 }398 }399 400 return true; /* No progress / error means canceled. */401 }402 403 bool Guest::callbackIsComplete(uint32_t uContextID)404 {405 AssertReturn(uContextID, true);406 407 ComPtr<IProgress> pProgress;408 {409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);410 411 CallbackMapIterConst it = mCallbackMap.find(uContextID);412 if (it != mCallbackMap.end())413 pProgress = it->second.pProgress;414 }415 416 if (pProgress)417 {418 BOOL fCompleted = FALSE;419 HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);420 if ( SUCCEEDED(hRC)421 && fCompleted)422 {423 return true;424 }425 }426 427 return false;428 }429 430 int Guest::callbackMoveForward(uint32_t uContextID, const char *pszMessage)431 {432 AssertReturn(uContextID, VERR_INVALID_PARAMETER);433 AssertPtrReturn(pszMessage, VERR_INVALID_PARAMETER);434 435 ComPtr<IProgress> pProgress;436 {437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);438 439 CallbackMapIterConst it = mCallbackMap.find(uContextID);440 if (it != mCallbackMap.end())441 pProgress = it->second.pProgress;442 }443 444 if (pProgress)445 {446 HRESULT hr = pProgress->SetNextOperation(Bstr(pszMessage).raw(), 1 /* Weight */);447 if (FAILED(hr))448 return VERR_CANCELLED;449 450 return VINF_SUCCESS;451 }452 453 return VERR_NOT_FOUND;454 }455 456 /**457 * Notifies a specified callback about its final status.458 *459 * @return IPRT status code.460 * @param uContextID461 * @param iRC462 * @param pszMessage463 */464 int Guest::callbackNotifyEx(uint32_t uContextID, int iRC, const char *pszMessage)465 {466 AssertReturn(uContextID, VERR_INVALID_PARAMETER);467 if (RT_FAILURE(iRC))468 AssertReturn(pszMessage, VERR_INVALID_PARAMETER);469 470 LogFlowFunc(("Checking whether callback (CID=%u) needs notification iRC=%Rrc, pszMsg=%s\n",471 uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));472 473 ComObjPtr<Progress> pProgress;474 {475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);476 477 CallbackMapIterConst it = mCallbackMap.find(uContextID);478 if (it != mCallbackMap.end())479 pProgress = it->second.pProgress;480 }481 482 #if 0483 BOOL fCanceled = FALSE;484 HRESULT hRC = pProgress->COMGETTER(Canceled)(&fCanceled);485 if ( SUCCEEDED(hRC)486 && fCanceled)487 {488 /* If progress already canceled do nothing here. */489 return VINF_SUCCESS;490 }491 #endif492 493 if (pProgress)494 {495 /*496 * Assume we didn't complete to make sure we clean up even if the497 * following call fails.498 */499 BOOL fCompleted = FALSE;500 HRESULT hRC = pProgress->COMGETTER(Completed)(&fCompleted);501 if ( SUCCEEDED(hRC)502 && !fCompleted)503 {504 LogFlowFunc(("Notifying callback with CID=%u, iRC=%Rrc, pszMsg=%s\n",505 uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));506 507 /*508 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only509 * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)510 * is disconnecting without having the chance to sending a status message before, so we511 * have to abort here to make sure the host never hangs/gets stuck while waiting for the512 * progress object to become signalled.513 */514 if (RT_SUCCESS(iRC))515 {516 hRC = pProgress->notifyComplete(S_OK);517 }518 else519 {520 hRC = pProgress->notifyComplete(VBOX_E_IPRT_ERROR /* Must not be S_OK. */,521 COM_IIDOF(IGuest),522 Guest::getStaticComponentName(),523 pszMessage);524 }525 526 LogFlowFunc(("Notified callback with CID=%u returned %Rhrc (0x%x)\n",527 uContextID, hRC, hRC));528 }529 else530 LogFlowFunc(("Callback with CID=%u already notified\n", uContextID));531 532 /*533 * Do *not* NULL pProgress here, because waiting function like executeProcess()534 * will still rely on this object for checking whether they have to give up!535 */536 }537 /* If pProgress is not found (anymore) that's fine.538 * Might be destroyed already. */539 return S_OK;540 }541 542 /**543 * Notifies all callbacks which are assigned to a certain guest PID to set a certain544 * return/error code and an optional (error) message.545 *546 * @return IPRT status code.547 * @param uGuestPID Guest PID to find all callbacks for.548 * @param iRC Return (error) code to set for the found callbacks.549 * @param pszMessage Optional (error) message to set.550 */551 int Guest::callbackNotifyAllForPID(uint32_t uGuestPID, int iRC, const char *pszMessage)552 {553 AssertReturn(uGuestPID, VERR_INVALID_PARAMETER);554 555 int vrc = VINF_SUCCESS;556 557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);558 559 CallbackMapIter it;560 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)561 {562 switch (it->second.mType)563 {564 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:565 break;566 567 /* When waiting for process output while the process is destroyed,568 * make sure we also destroy the actual waiting operation (internal progress object)569 * in order to not block the caller. */570 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:571 {572 PCALLBACKDATAEXECOUT pItData = (PCALLBACKDATAEXECOUT)it->second.pvData;573 AssertPtr(pItData);574 if (pItData->u32PID == uGuestPID)575 vrc = callbackNotifyEx(it->first, iRC, pszMessage);576 break;577 }578 579 /* When waiting for injecting process input while the process is destroyed,580 * make sure we also destroy the actual waiting operation (internal progress object)581 * in order to not block the caller. */582 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS:583 {584 PCALLBACKDATAEXECINSTATUS pItData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;585 AssertPtr(pItData);586 if (pItData->u32PID == uGuestPID)587 vrc = callbackNotifyEx(it->first, iRC, pszMessage);588 break;589 }590 591 default:592 vrc = VERR_INVALID_PARAMETER;593 AssertMsgFailed(("Unknown callback type %d, iRC=%d, message=%s\n",594 it->second.mType, iRC, pszMessage ? pszMessage : "<No message given>"));595 break;596 }597 598 if (RT_FAILURE(vrc))599 break;600 }601 602 return vrc;603 }604 605 int Guest::callbackNotifyComplete(uint32_t uContextID)606 {607 return callbackNotifyEx(uContextID, S_OK, NULL /* No message */);608 }609 610 /**611 * Waits for a callback (using its context ID) to complete.612 *613 * @return IPRT status code.614 * @param uContextID Context ID to wait for.615 * @param lStage Stage to wait for. Specify -1 if no staging is present/required.616 * Specifying a stage is only needed if there's a multi operation progress617 * object to wait for.618 * @param lTimeout Timeout (in ms) to wait for.619 */620 int Guest::callbackWaitForCompletion(uint32_t uContextID, LONG lStage, LONG lTimeout)621 {622 AssertReturn(uContextID, VERR_INVALID_PARAMETER);623 624 /*625 * Wait for the HGCM low level callback until the process626 * has been started (or something went wrong). This is necessary to627 * get the PID.628 */629 630 int vrc = VINF_SUCCESS;631 ComPtr<IProgress> pProgress;632 {633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);634 635 CallbackMapIterConst it = mCallbackMap.find(uContextID);636 if (it != mCallbackMap.end())637 pProgress = it->second.pProgress;638 else639 vrc = VERR_NOT_FOUND;640 }641 642 if (RT_SUCCESS(vrc))643 {644 LogFlowFunc(("Waiting for callback completion (CID=%u, Stage=%RI32, timeout=%RI32ms) ...\n",645 uContextID, lStage, lTimeout));646 HRESULT rc;647 if (lStage < 0)648 rc = pProgress->WaitForCompletion(lTimeout);649 else650 rc = pProgress->WaitForOperationCompletion((ULONG)lStage, lTimeout);651 if (SUCCEEDED(rc))652 {653 if (!callbackIsComplete(uContextID))654 vrc = callbackIsCanceled(uContextID)655 ? VERR_CANCELLED : VINF_SUCCESS;656 }657 else658 vrc = VERR_TIMEOUT;659 }660 661 LogFlowFunc(("Callback (CID=%u) completed with rc=%Rrc\n",662 uContextID, vrc));663 return vrc;664 }665 666 /**667 49 * Static callback function for receiving updates on guest control commands 668 50 * from the guest. Acts as a dispatcher for the actual class instance. … … 673 55 * 674 56 */ 57 /* static */ 675 58 DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension, 676 59 uint32_t u32Function, … … 742 125 } 743 126 744 #ifdef DEBUG745 /* @todo Don't use legacy stuff in debug mode. Remove for final! */746 return rc;747 #endif748 749 /* Legacy handling. */750 switch (u32Function)751 {752 case GUEST_DISCONNECTED:753 {754 PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);755 AssertPtr(pCBData);756 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbParms, VERR_INVALID_PARAMETER);757 AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);758 759 rc = pGuest->notifyCtrlClientDisconnected(u32Function, pCBData);760 break;761 }762 763 case GUEST_EXEC_SEND_STATUS:764 {765 PCALLBACKDATAEXECSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);766 AssertPtr(pCBData);767 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);768 AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);769 770 rc = pGuest->notifyCtrlExecStatus(u32Function, pCBData);771 break;772 }773 774 case GUEST_EXEC_SEND_OUTPUT:775 {776 PCALLBACKDATAEXECOUT pCBData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvParms);777 AssertPtr(pCBData);778 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbParms, VERR_INVALID_PARAMETER);779 AssertReturn(CALLBACKDATAMAGIC_EXEC_OUT == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);780 781 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);782 break;783 }784 785 case GUEST_EXEC_SEND_INPUT_STATUS:786 {787 PCALLBACKDATAEXECINSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvParms);788 AssertPtr(pCBData);789 AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbParms, VERR_INVALID_PARAMETER);790 AssertReturn(CALLBACKDATAMAGIC_EXEC_IN_STATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);791 792 rc = pGuest->notifyCtrlExecInStatus(u32Function, pCBData);793 break;794 }795 796 default:797 /* Silently ignore not implemented functions. */798 rc = VERR_NOT_IMPLEMENTED;799 break;800 }801 802 127 LogFlowFuncLeaveRC(rc); 803 128 return rc; 804 129 } 805 806 /* Function for handling the execution start/termination notification. */807 /* Callback can be called several times. */808 int Guest::notifyCtrlExecStatus(uint32_t u32Function,809 PCALLBACKDATAEXECSTATUS pData)810 {811 AssertReturn(u32Function, VERR_INVALID_PARAMETER);812 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);813 814 uint32_t uContextID = pData->hdr.u32ContextID;815 /* The context ID might be 0 in case the guest was not able to fetch816 * actual command. So we can't do much now but report an error. */817 818 /* Scope write locks as much as possible. */819 {820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);821 822 LogFlowFunc(("Execution status (CID=%u, pData=0x%p)\n",823 uContextID, pData));824 825 PCALLBACKDATAEXECSTATUS pCallbackData =826 (PCALLBACKDATAEXECSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);827 if (pCallbackData)828 {829 pCallbackData->u32PID = pData->u32PID;830 pCallbackData->u32Status = pData->u32Status;831 pCallbackData->u32Flags = pData->u32Flags;832 /** @todo Copy void* buffer contents? */833 }834 /* If pCallbackData is NULL this might be an old request for which no user data835 * might exist anymore. */836 }837 838 int vrc = VINF_SUCCESS; /* Function result. */839 int rcCallback = VINF_SUCCESS; /* Callback result. */840 Utf8Str errMsg;841 842 /* Was progress canceled before? */843 bool fCanceled = callbackIsCanceled(uContextID);844 if (!fCanceled)845 {846 /* Handle process map. This needs to be done first in order to have a valid847 * map in case some callback gets notified a bit below. */848 849 uint32_t uHostPID = 0;850 851 /* Note: PIDs never get removed here in case the guest process signalled its852 * end; instead the next call of GetProcessStatus() will remove the PID853 * from the process map after we got the process' final (exit) status.854 * See waitpid() for an example. */855 if (pData->u32PID) /* Only add/change a process if it has a valid PID (>0). */856 {857 uHostPID = callbackGetHostPID(uContextID);858 Assert(uHostPID);859 860 switch (pData->u32Status)861 {862 /* Just reach through flags. */863 case PROC_STS_TES:864 case PROC_STS_TOK:865 vrc = processSetStatus(uHostPID, pData->u32PID,866 (ExecuteProcessStatus_T)pData->u32Status,867 0 /* Exit code */, pData->u32Flags);868 break;869 /* Interprete u32Flags as the guest process' exit code. */870 default:871 vrc = processSetStatus(uHostPID, pData->u32PID,872 (ExecuteProcessStatus_T)pData->u32Status,873 pData->u32Flags /* Exit code */, 0 /* Flags */);874 break;875 }876 }877 878 if (RT_SUCCESS(vrc))879 {880 /* Do progress handling. */881 switch (pData->u32Status)882 {883 case PROC_STS_STARTED:884 vrc = callbackMoveForward(uContextID, Guest::tr("Waiting for process to exit ..."));885 LogRel(("Guest process (PID %u) started\n", pData->u32PID)); /** @todo Add process name */886 break;887 888 case PROC_STS_TEN: /* Terminated normally. */889 vrc = callbackNotifyComplete(uContextID);890 LogRel(("Guest process (PID %u) exited normally (exit code: %u)\n",891 pData->u32PID, pData->u32Flags)); /** @todo Add process name */892 break;893 894 case PROC_STS_TEA: /* Terminated abnormally. */895 LogRel(("Guest process (PID %u) terminated abnormally (exit code: %u)\n",896 pData->u32PID, pData->u32Flags)); /** @todo Add process name */897 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),898 pData->u32Flags);899 rcCallback = VERR_GENERAL_FAILURE; /** @todo */900 break;901 902 case PROC_STS_TES: /* Terminated through signal. */903 LogRel(("Guest process (PID %u) terminated through signal (exit code: %u)\n",904 pData->u32PID, pData->u32Flags)); /** @todo Add process name */905 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),906 pData->u32Flags);907 rcCallback = VERR_GENERAL_FAILURE; /** @todo */908 break;909 910 case PROC_STS_TOK:911 LogRel(("Guest process (PID %u) timed out and was killed\n", pData->u32PID)); /** @todo Add process name */912 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));913 rcCallback = VERR_TIMEOUT;914 break;915 916 case PROC_STS_TOA:917 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pData->u32PID)); /** @todo Add process name */918 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));919 rcCallback = VERR_TIMEOUT;920 break;921 922 case PROC_STS_DWN:923 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pData->u32PID)); /** @todo Add process name */924 /*925 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to926 * our progress object. This is helpful for waiters which rely on the success of our progress object927 * even if the executed process was killed because the system/VBoxService is shutting down.928 *929 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().930 */931 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)932 {933 vrc = callbackNotifyComplete(uContextID);934 }935 else936 {937 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));938 rcCallback = VERR_CANCELLED;939 }940 break;941 942 case PROC_STS_ERROR:943 {944 Utf8Str errDetail;945 if (pData->u32PID)946 {947 errDetail = Utf8StrFmt(Guest::tr("Guest process (PID %u) could not be started because of rc=%Rrc"),948 pData->u32PID, pData->u32Flags);949 }950 else951 {952 switch (pData->u32Flags) /* u32Flags member contains the IPRT error code from guest side. */953 {954 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */955 errDetail = Utf8StrFmt(Guest::tr("The specified file was not found on guest"));956 break;957 958 case VERR_PATH_NOT_FOUND:959 errDetail = Utf8StrFmt(Guest::tr("Could not resolve path to specified file was not found on guest"));960 break;961 962 case VERR_BAD_EXE_FORMAT:963 errDetail = Utf8StrFmt(Guest::tr("The specified file is not an executable format on guest"));964 break;965 966 case VERR_AUTHENTICATION_FAILURE:967 errDetail = Utf8StrFmt(Guest::tr("The specified user was not able to logon on guest"));968 break;969 970 case VERR_TIMEOUT:971 errDetail = Utf8StrFmt(Guest::tr("The guest did not respond within time"));972 break;973 974 case VERR_CANCELLED:975 errDetail = Utf8StrFmt(Guest::tr("The execution operation was canceled"));976 break;977 978 case VERR_PERMISSION_DENIED:979 errDetail = Utf8StrFmt(Guest::tr("Invalid user/password credentials"));980 break;981 982 case VERR_MAX_PROCS_REACHED:983 errDetail = Utf8StrFmt(Guest::tr("Guest process could not be started because maximum number of parallel guest processes has been reached"));984 break;985 986 default:987 errDetail = Utf8StrFmt(Guest::tr("Guest process reported error %Rrc"), pData->u32Flags);988 break;989 }990 }991 992 errMsg = Utf8StrFmt(Guest::tr("Process execution failed: "), pData->u32Flags) + errDetail;993 rcCallback = pData->u32Flags; /* Report back guest rc. */994 995 LogRel((errMsg.c_str()));996 997 break;998 }999 1000 default:1001 vrc = VERR_INVALID_PARAMETER;1002 break;1003 }1004 }1005 }1006 else1007 {1008 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));1009 rcCallback = VERR_CANCELLED;1010 }1011 1012 /* Do we need to handle the callback error? */1013 if (RT_FAILURE(rcCallback))1014 {1015 AssertMsg(!errMsg.isEmpty(), ("Error message must not be empty!\n"));1016 1017 if (uContextID)1018 {1019 /* Notify all callbacks which are still waiting on something1020 * which is related to the current PID. */1021 if (pData->u32PID)1022 {1023 int rc2 = callbackNotifyAllForPID(pData->u32PID, rcCallback, errMsg.c_str());1024 if (RT_FAILURE(rc2))1025 {1026 LogFlowFunc(("Failed to notify other callbacks for PID=%u\n",1027 pData->u32PID));1028 if (RT_SUCCESS(vrc))1029 vrc = rc2;1030 }1031 }1032 1033 /* Let the caller know what went wrong ... */1034 int rc2 = callbackNotifyEx(uContextID, rcCallback, errMsg.c_str());1035 if (RT_FAILURE(rc2))1036 {1037 LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n",1038 uContextID, pData->u32PID));1039 if (RT_SUCCESS(vrc))1040 vrc = rc2;1041 }1042 }1043 else1044 {1045 /* Since we don't know which context exactly failed all we can do is to shutdown1046 * all contexts ... */1047 errMsg = Utf8StrFmt(Guest::tr("Client reported critical error %Rrc -- shutting down"),1048 pData->u32Flags);1049 1050 /* Cancel all callbacks. */1051 CallbackMapIter it;1052 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)1053 {1054 int rc2 = callbackNotifyEx(it->first, VERR_CANCELLED,1055 errMsg.c_str());1056 AssertRC(rc2);1057 }1058 }1059 1060 LogFlowFunc(("Process (CID=%u, status=%u) reported: %s\n",1061 uContextID, pData->u32Status, errMsg.c_str()));1062 }1063 LogFlowFunc(("Returned with rc=%Rrc, rcCallback=%Rrc\n",1064 vrc, rcCallback));1065 return vrc;1066 }1067 1068 /* Function for handling the execution output notification. */1069 int Guest::notifyCtrlExecOut(uint32_t u32Function,1070 PCALLBACKDATAEXECOUT pData)1071 {1072 AssertReturn(u32Function, VERR_INVALID_PARAMETER);1073 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);1074 1075 uint32_t uContextID = pData->hdr.u32ContextID;1076 Assert(uContextID);1077 1078 /* Scope write locks as much as possible. */1079 {1080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1081 1082 LogFlowFunc(("Output status (CID=%u, pData=0x%p)\n",1083 uContextID, pData));1084 1085 PCALLBACKDATAEXECOUT pCallbackData =1086 (PCALLBACKDATAEXECOUT)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);1087 if (pCallbackData)1088 {1089 pCallbackData->u32PID = pData->u32PID;1090 pCallbackData->u32HandleId = pData->u32HandleId;1091 pCallbackData->u32Flags = pData->u32Flags;1092 1093 /* Make sure we really got something! */1094 if ( pData->cbData1095 && pData->pvData)1096 {1097 callbackFreeUserData(pCallbackData->pvData);1098 1099 /* Allocate data buffer and copy it */1100 pCallbackData->pvData = RTMemAlloc(pData->cbData);1101 pCallbackData->cbData = pData->cbData;1102 1103 AssertReturn(pCallbackData->pvData, VERR_NO_MEMORY);1104 memcpy(pCallbackData->pvData, pData->pvData, pData->cbData);1105 }1106 else /* Nothing received ... */1107 {1108 pCallbackData->pvData = NULL;1109 pCallbackData->cbData = 0;1110 }1111 }1112 /* If pCallbackData is NULL this might be an old request for which no user data1113 * might exist anymore. */1114 }1115 1116 int vrc;1117 if (callbackIsCanceled(pData->u32PID))1118 {1119 vrc = callbackNotifyEx(uContextID, VERR_CANCELLED,1120 Guest::tr("The output operation was canceled"));1121 }1122 else1123 vrc = callbackNotifyComplete(uContextID);1124 1125 return vrc;1126 }1127 1128 /* Function for handling the execution input status notification. */1129 int Guest::notifyCtrlExecInStatus(uint32_t u32Function,1130 PCALLBACKDATAEXECINSTATUS pData)1131 {1132 AssertReturn(u32Function, VERR_INVALID_PARAMETER);1133 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);1134 1135 uint32_t uContextID = pData->hdr.u32ContextID;1136 Assert(uContextID);1137 1138 /* Scope write locks as much as possible. */1139 {1140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1141 1142 LogFlowFunc(("Input status (CID=%u, pData=0x%p)\n",1143 uContextID, pData));1144 1145 PCALLBACKDATAEXECINSTATUS pCallbackData =1146 (PCALLBACKDATAEXECINSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);1147 if (pCallbackData)1148 {1149 /* Save bytes processed. */1150 pCallbackData->cbProcessed = pData->cbProcessed;1151 pCallbackData->u32Status = pData->u32Status;1152 pCallbackData->u32Flags = pData->u32Flags;1153 pCallbackData->u32PID = pData->u32PID;1154 }1155 /* If pCallbackData is NULL this might be an old request for which no user data1156 * might exist anymore. */1157 }1158 1159 return callbackNotifyComplete(uContextID);1160 }1161 1162 int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,1163 PCALLBACKDATACLIENTDISCONNECTED pData)1164 {1165 /* u32Function is 0. */1166 AssertPtrReturn(pData, VERR_INVALID_PARAMETER);1167 1168 uint32_t uContextID = pData->hdr.u32ContextID;1169 Assert(uContextID);1170 1171 LogFlowFunc(("Client disconnected (CID=%u)\n,", uContextID));1172 1173 return callbackNotifyEx(uContextID, VERR_CANCELLED,1174 Guest::tr("Client disconnected"));1175 }1176 1177 uint32_t Guest::processGetGuestPID(uint32_t uHostPID)1178 {1179 AssertReturn(uHostPID, 0);1180 1181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1182 1183 GuestProcessMapIter it = mGuestProcessMap.find(uHostPID);1184 if (it == mGuestProcessMap.end())1185 return 0;1186 1187 return it->second.mGuestPID;1188 }1189 1190 /**1191 * Gets guest process information. Removes the process from the map1192 * after the process was marked as exited/terminated.1193 *1194 * @return IPRT status code.1195 * @param uHostPID Host PID of guest process to get status for.1196 * @param pProcess Where to store the process information. Optional.1197 * @param fRemove Flag indicating whether to remove the1198 * process from the map when process marked a1199 * exited/terminated.1200 */1201 int Guest::processGetStatus(uint32_t uHostPID, PVBOXGUESTCTRL_PROCESS pProcess,1202 bool fRemove)1203 {1204 AssertReturn(uHostPID, VERR_INVALID_PARAMETER);1205 1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1207 1208 GuestProcessMapIter it = mGuestProcessMap.find(uHostPID);1209 if (it != mGuestProcessMap.end())1210 {1211 if (pProcess)1212 {1213 pProcess->mGuestPID = it->second.mGuestPID;1214 pProcess->mStatus = it->second.mStatus;1215 pProcess->mExitCode = it->second.mExitCode;1216 pProcess->mFlags = it->second.mFlags;1217 }1218 1219 /* Only remove processes from our map when they signalled their final1220 * status. */1221 if ( fRemove1222 && ( it->second.mStatus != ExecuteProcessStatus_Undefined1223 && it->second.mStatus != ExecuteProcessStatus_Started))1224 {1225 mGuestProcessMap.erase(it);1226 }1227 1228 return VINF_SUCCESS;1229 }1230 1231 return VERR_NOT_FOUND;1232 }1233 1234 /**1235 * Sets the current status of a guest process.1236 *1237 * @return IPRT status code.1238 * @param uHostPID Host PID of guest process to set status (and guest PID) for.1239 * @param uGuestPID Guest PID to assign to the host PID.1240 * @param enmStatus Current status to set.1241 * @param uExitCode Exit code (if any).1242 * @param uFlags Additional flags.1243 */1244 int Guest::processSetStatus(uint32_t uHostPID, uint32_t uGuestPID,1245 ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)1246 {1247 AssertReturn(uHostPID, VERR_INVALID_PARAMETER);1248 /* Assigning a guest PID is optional. */1249 1250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1251 1252 GuestProcessMapIter it = mGuestProcessMap.find(uHostPID);1253 if (it != mGuestProcessMap.end())1254 {1255 it->second.mGuestPID = uGuestPID;1256 it->second.mStatus = enmStatus;1257 it->second.mExitCode = uExitCode;1258 it->second.mFlags = uFlags;1259 }1260 else1261 {1262 VBOXGUESTCTRL_PROCESS process;1263 1264 /* uGuestPID is optional -- the caller could call this function1265 * before the guest process actually was started and a (valid) guest PID1266 * was returned. */1267 process.mGuestPID = uGuestPID;1268 process.mStatus = enmStatus;1269 process.mExitCode = uExitCode;1270 process.mFlags = uFlags;1271 1272 mGuestProcessMap[uHostPID] = process;1273 }1274 1275 return VINF_SUCCESS;1276 }1277 1278 HRESULT Guest::setErrorCompletion(int rc)1279 {1280 HRESULT hRC;1281 if (rc == VERR_NOT_FOUND)1282 hRC = setErrorNoLog(VBOX_E_VM_ERROR,1283 tr("VMM device is not available (is the VM running?)"));1284 else if (rc == VERR_CANCELLED)1285 hRC = setErrorNoLog(VBOX_E_IPRT_ERROR,1286 tr("Process execution has been canceled"));1287 else if (rc == VERR_TIMEOUT)1288 hRC= setErrorNoLog(VBOX_E_IPRT_ERROR,1289 tr("The guest did not respond within time"));1290 else1291 hRC = setErrorNoLog(E_UNEXPECTED,1292 tr("Waiting for completion failed with error %Rrc"), rc);1293 return hRC;1294 }1295 1296 HRESULT Guest::setErrorFromProgress(ComPtr<IProgress> pProgress)1297 {1298 BOOL fCompleted;1299 HRESULT rc = pProgress->COMGETTER(Completed)(&fCompleted);1300 ComAssertComRC(rc);1301 1302 LONG rcProc = S_OK;1303 Utf8Str strError;1304 1305 if (!fCompleted)1306 {1307 BOOL fCanceled;1308 rc = pProgress->COMGETTER(Canceled)(&fCanceled);1309 ComAssertComRC(rc);1310 1311 strError = fCanceled ? Utf8StrFmt(Guest::tr("Process execution was canceled"))1312 : Utf8StrFmt(Guest::tr("Process neither completed nor canceled; this shouldn't happen"));1313 }1314 else1315 {1316 rc = pProgress->COMGETTER(ResultCode)(&rcProc);1317 ComAssertComRC(rc);1318 1319 if (FAILED(rcProc))1320 {1321 ProgressErrorInfo info(pProgress);1322 strError = info.getText();1323 }1324 }1325 1326 if (FAILED(rcProc))1327 {1328 AssertMsg(!strError.isEmpty(), ("Error message must not be empty!\n"));1329 return setErrorInternal(rcProc,1330 this->getClassIID(),1331 this->getComponentName(),1332 strError,1333 false /* aWarning */,1334 false /* aLogIt */);1335 }1336 1337 return S_OK;1338 }1339 1340 HRESULT Guest::setErrorHGCM(int rc)1341 {1342 HRESULT hRC;1343 if (rc == VERR_INVALID_VM_HANDLE)1344 hRC = setErrorNoLog(VBOX_E_VM_ERROR,1345 tr("VMM device is not available (is the VM running?)"));1346 else if (rc == VERR_NOT_FOUND)1347 hRC = setErrorNoLog(VBOX_E_IPRT_ERROR,1348 tr("The guest execution service is not ready (yet)"));1349 else if (rc == VERR_HGCM_SERVICE_NOT_FOUND)1350 hRC= setErrorNoLog(VBOX_E_IPRT_ERROR,1351 tr("The guest execution service is not available"));1352 else /* HGCM call went wrong. */1353 hRC = setErrorNoLog(E_UNEXPECTED,1354 tr("The HGCM call failed with error %Rrc"), rc);1355 return hRC;1356 }1357 130 #endif /* VBOX_WITH_GUEST_CONTROL */ 1358 1359 STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,1360 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),1361 IN_BSTR aUsername, IN_BSTR aPassword,1362 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)1363 {1364 /** @todo r=bird: Eventually we should clean up all the timeout parameters1365 * in the API and have the same way of specifying infinite waits! */1366 #ifndef VBOX_WITH_GUEST_CONTROL1367 ReturnComNotImplemented();1368 #else /* VBOX_WITH_GUEST_CONTROL */1369 using namespace guestControl;1370 1371 CheckComArgStrNotEmptyOrNull(aCommand);1372 CheckComArgOutPointerValid(aPID);1373 CheckComArgOutPointerValid(aProgress);1374 1375 /* Do not allow anonymous executions (with system rights). */1376 if (RT_UNLIKELY((aUsername) == NULL || *(aUsername) == '\0'))1377 return setError(E_INVALIDARG, tr("No user name specified"));1378 1379 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",1380 Utf8Str(aCommand).c_str(), Utf8Str(aUsername).c_str()));1381 1382 return executeProcessInternal(aCommand, aFlags, ComSafeArrayInArg(aArguments),1383 ComSafeArrayInArg(aEnvironment),1384 aUsername, aPassword, aTimeoutMS, aPID, aProgress, NULL /* rc */);1385 #endif1386 }1387 1388 #ifdef VBOX_WITH_GUEST_CONTROL1389 /**1390 * Executes and waits for an internal tool (that is, a tool which is integrated into1391 * VBoxService, beginning with "vbox_" (e.g. "vbox_ls")) to finish its operation.1392 *1393 * @return HRESULT1394 * @param aTool Name of tool to execute.1395 * @param aDescription Friendly description of the operation.1396 * @param aFlags Execution flags.1397 * @param aUsername Username to execute tool under.1398 * @param aPassword The user's password.1399 * @param uFlagsToAdd ExecuteProcessFlag flags to add to the execution operation.1400 * @param aProgress Pointer which receives the tool's progress object. Optional.1401 * @param aPID Pointer which receives the tool's PID. Optional.1402 */1403 HRESULT Guest::executeAndWaitForTool(IN_BSTR aTool, IN_BSTR aDescription,1404 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),1405 IN_BSTR aUsername, IN_BSTR aPassword,1406 ULONG uFlagsToAdd,1407 GuestCtrlStreamObjects *pObjStdOut, GuestCtrlStreamObjects *pObjStdErr,1408 IProgress **aProgress, ULONG *aPID)1409 {1410 ComPtr<IProgress> pProgress;1411 ULONG uPID;1412 ULONG uFlags = ExecuteProcessFlag_Hidden;1413 if (uFlagsToAdd)1414 uFlags |= uFlagsToAdd;1415 1416 bool fParseOutput = false;1417 if ( ( (uFlags & ExecuteProcessFlag_WaitForStdOut)1418 && pObjStdOut)1419 || ( (uFlags & ExecuteProcessFlag_WaitForStdErr)1420 && pObjStdErr))1421 {1422 fParseOutput = true;1423 }1424 1425 HRESULT hr = ExecuteProcess(aTool,1426 uFlags,1427 ComSafeArrayInArg(aArguments),1428 ComSafeArrayInArg(aEnvironment),1429 aUsername, aPassword,1430 0 /* No timeout */,1431 &uPID, pProgress.asOutParam());1432 if (SUCCEEDED(hr))1433 {1434 /* Wait for tool being started. */1435 hr = pProgress->WaitForOperationCompletion( 0 /* Stage, starting the process */,1436 -1 /* No timeout */);1437 }1438 1439 if ( SUCCEEDED(hr)1440 && !(uFlags & ExecuteProcessFlag_WaitForProcessStartOnly))1441 {1442 if (!fParseOutput)1443 {1444 if ( !(uFlags & ExecuteProcessFlag_WaitForStdOut)1445 && !(uFlags & ExecuteProcessFlag_WaitForStdErr))1446 {1447 hr = executeWaitForExit(uPID, pProgress, 0 /* No timeout */);1448 }1449 }1450 else1451 {1452 BOOL fCompleted;1453 while ( SUCCEEDED(pProgress->COMGETTER(Completed)(&fCompleted))1454 && !fCompleted)1455 {1456 BOOL fCanceled;1457 hr = pProgress->COMGETTER(Canceled)(&fCanceled);1458 AssertComRC(hr);1459 if (fCanceled)1460 {1461 hr = setErrorNoLog(VBOX_E_IPRT_ERROR,1462 tr("%s was cancelled"), Utf8Str(aDescription).c_str());1463 break;1464 }1465 1466 if ( (uFlags & ExecuteProcessFlag_WaitForStdOut)1467 && pObjStdOut)1468 {1469 hr = executeStreamParse(uPID, ProcessOutputFlag_None /* StdOut */, *pObjStdOut);1470 }1471 1472 if ( (uFlags & ExecuteProcessFlag_WaitForStdErr)1473 && pObjStdErr)1474 {1475 hr = executeStreamParse(uPID, ProcessOutputFlag_StdErr, *pObjStdErr);1476 }1477 1478 if (FAILED(hr))1479 break;1480 }1481 }1482 }1483 1484 if (SUCCEEDED(hr))1485 {1486 if (aProgress)1487 {1488 /* Return the progress to the caller. */1489 pProgress.queryInterfaceTo(aProgress);1490 }1491 else if (!pProgress.isNull())1492 pProgress.setNull();1493 1494 if (aPID)1495 *aPID = uPID;1496 }1497 1498 return hr;1499 }1500 1501 /**1502 * TODO1503 *1504 * @return HRESULT1505 * @param aObjName1506 * @param pStreamBlock1507 * @param pObjInfo1508 * @param enmAddAttribs1509 */1510 int Guest::executeStreamQueryFsObjInfo(IN_BSTR aObjName,1511 GuestProcessStreamBlock &streamBlock,1512 PRTFSOBJINFO pObjInfo,1513 RTFSOBJATTRADD enmAddAttribs)1514 {1515 Utf8Str Utf8ObjName(aObjName);1516 int64_t iVal;1517 int rc = streamBlock.GetInt64Ex("st_size", &iVal);1518 if (RT_SUCCESS(rc))1519 pObjInfo->cbObject = iVal;1520 /** @todo Add more stuff! */1521 return rc;1522 }1523 1524 /**1525 * Tries to drain the guest's output and fill it into1526 * a guest process stream object for later usage.1527 *1528 * @todo What's about specifying stderr?1529 * @return IPRT status code.1530 * @param aPID PID of process to get the output from.1531 * @param aFlags Which stream to drain (stdout or stderr).1532 * @param pStream Pointer to guest process stream to fill. If NULL,1533 * data goes to /dev/null.1534 */1535 int Guest::executeStreamDrain(ULONG aPID, ULONG aFlags, GuestProcessStream *pStream)1536 {1537 AssertReturn(aPID, VERR_INVALID_PARAMETER);1538 /* pStream is optional. */1539 1540 int rc = VINF_SUCCESS;1541 for (;;)1542 {1543 SafeArray<BYTE> aData;1544 HRESULT hr = getProcessOutputInternal(aPID, aFlags,1545 0 /* Infinite timeout */,1546 _64K, ComSafeArrayAsOutParam(aData), &rc);1547 if (RT_SUCCESS(rc))1548 {1549 if ( pStream1550 && aData.size())1551 {1552 rc = pStream->AddData(aData.raw(), aData.size());1553 if (RT_UNLIKELY(RT_FAILURE(rc)))1554 break;1555 }1556 1557 continue; /* Try one more time. */1558 }1559 else /* No more output and/or error! */1560 {1561 if ( rc == VERR_NOT_FOUND1562 || rc == VERR_BROKEN_PIPE)1563 {1564 rc = VINF_SUCCESS;1565 }1566 1567 break;1568 }1569 }1570 1571 return rc;1572 }1573 1574 /**1575 * Tries to retrieve the next stream block of a given stream and1576 * drains the process output only as much as needed to get this next1577 * stream block.1578 *1579 * @return IPRT status code.1580 * @param ulPID1581 * @param ulFlags1582 * @param stream1583 * @param streamBlock1584 */1585 int Guest::executeStreamGetNextBlock(ULONG ulPID,1586 ULONG ulFlags,1587 GuestProcessStream &stream,1588 GuestProcessStreamBlock &streamBlock)1589 {1590 AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);1591 1592 LogFlowFunc(("Getting next stream block of PID=%u, Flags=%u; cbStrmSize=%u, cbStrmOff=%u\n",1593 ulPID, ulFlags, stream.GetSize(), stream.GetOffset()));1594 1595 int rc;1596 1597 uint32_t cPairs = 0;1598 bool fDrainStream = true;1599 1600 do1601 {1602 rc = stream.ParseBlock(streamBlock);1603 LogFlowFunc(("Parsing block rc=%Rrc, strmBlockCnt=%ld\n",1604 rc, streamBlock.GetCount()));1605 1606 if (RT_FAILURE(rc)) /* More data needed or empty buffer? */1607 {1608 if (fDrainStream)1609 {1610 SafeArray<BYTE> aData;1611 HRESULT hr = getProcessOutputInternal(ulPID, ulFlags,1612 0 /* Infinite timeout */,1613 _64K, ComSafeArrayAsOutParam(aData), &rc);1614 if (SUCCEEDED(hr))1615 {1616 LogFlowFunc(("Got %ld bytes of additional data\n", aData.size()));1617 1618 if (RT_FAILURE(rc))1619 {1620 if (rc == VERR_BROKEN_PIPE)1621 rc = VINF_SUCCESS; /* No more data because process already ended. */1622 break;1623 }1624 1625 if (aData.size())1626 {1627 rc = stream.AddData(aData.raw(), aData.size());1628 if (RT_UNLIKELY(RT_FAILURE(rc)))1629 break;1630 }1631 1632 /* Reset found pairs to not break out too early and let all the new1633 * data to be parsed as well. */1634 cPairs = 0;1635 continue; /* Try one more time. */1636 }1637 else1638 {1639 LogFlowFunc(("Getting output returned hr=%Rhrc\n", hr));1640 1641 /* No more output to drain from stream. */1642 if (rc == VERR_NOT_FOUND)1643 {1644 fDrainStream = false;1645 continue;1646 }1647 1648 break;1649 }1650 }1651 else1652 {1653 /* We haved drained the stream as much as we can and reached the1654 * end of our stream buffer -- that means that there simply is no1655 * stream block anymore, which is ok. */1656 if (rc == VERR_NO_DATA)1657 rc = VINF_SUCCESS;1658 break;1659 }1660 }1661 else1662 cPairs = streamBlock.GetCount();1663 }1664 while (!cPairs);1665 1666 LogFlowFunc(("Returned with strmBlockCnt=%ld, cPairs=%ld, rc=%Rrc\n",1667 streamBlock.GetCount(), cPairs, rc));1668 return rc;1669 }1670 1671 /**1672 * Tries to get the next upcoming value block from a started guest process1673 * by first draining its output and then processing the received guest stream.1674 *1675 * @return IPRT status code.1676 * @param ulPID PID of process to get/parse the output from.1677 * @param ulFlags ?1678 * @param stream Reference to process stream object to use.1679 * @param streamBlock Reference that receives the next stream block data.1680 *1681 */1682 int Guest::executeStreamParseNextBlock(ULONG ulPID,1683 ULONG ulFlags,1684 GuestProcessStream &stream,1685 GuestProcessStreamBlock &streamBlock)1686 {1687 AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);1688 1689 int rc;1690 do1691 {1692 rc = stream.ParseBlock(streamBlock);1693 if (RT_FAILURE(rc))1694 break;1695 }1696 while (!streamBlock.GetCount());1697 1698 return rc;1699 }1700 1701 /**1702 * Gets output from a formerly started guest process, tries to parse all of its guest1703 * stream (as long as data is available) and returns a stream object which can contain1704 * multiple stream blocks (which in turn then can contain key=value pairs).1705 *1706 * @return HRESULT1707 * @param ulPID PID of process to get/parse the output from.1708 * @param ulFlags ?1709 * @param streamObjects Reference to a guest stream object structure for1710 * storing the parsed data.1711 */1712 HRESULT Guest::executeStreamParse(ULONG ulPID, ULONG ulFlags, GuestCtrlStreamObjects &streamObjects)1713 {1714 GuestProcessStream stream;1715 int rc = executeStreamDrain(ulPID, ulFlags, &stream);1716 if (RT_SUCCESS(rc))1717 {1718 do1719 {1720 /* Try to parse the stream output we gathered until now. If we still need more1721 * data the parsing routine will tell us and we just do another poll round. */1722 GuestProcessStreamBlock curBlock;1723 rc = executeStreamParseNextBlock(ulPID, ulFlags, stream, curBlock);1724 if (RT_SUCCESS(rc))1725 streamObjects.push_back(curBlock);1726 } while (RT_SUCCESS(rc));1727 1728 if (rc == VERR_NO_DATA) /* End of data reached. */1729 rc = VINF_SUCCESS;1730 }1731 1732 if (RT_FAILURE(rc))1733 return setError(VBOX_E_IPRT_ERROR,1734 tr("Error while parsing guest output (%Rrc)"), rc);1735 return rc;1736 }1737 1738 /**1739 * Waits for a fomerly started guest process to exit using its progress1740 * object and returns its final status. Returns E_ABORT if guest process1741 * was canceled.1742 *1743 * @return IPRT status code.1744 * @param uPID PID of guest process to wait for.1745 * @param pProgress Progress object to wait for.1746 * @param uTimeoutMS Timeout (in ms) for waiting; use 0 for1747 * an indefinite timeout.1748 * @param pRetStatus Pointer where to store the final process1749 * status. Optional.1750 * @param puRetExitCode Pointer where to store the final process1751 * exit code. Optional.1752 */1753 HRESULT Guest::executeWaitForExit(ULONG uPID, ComPtr<IProgress> pProgress, ULONG uTimeoutMS)1754 {1755 HRESULT rc = S_OK;1756 1757 BOOL fCanceled = FALSE;1758 if ( SUCCEEDED(pProgress->COMGETTER(Canceled(&fCanceled)))1759 && fCanceled)1760 {1761 return E_ABORT;1762 }1763 1764 BOOL fCompleted = FALSE;1765 if ( SUCCEEDED(pProgress->COMGETTER(Completed(&fCompleted)))1766 && !fCompleted)1767 {1768 rc = pProgress->WaitForCompletion( !uTimeoutMS1769 ? -1 /* No timeout */1770 : uTimeoutMS);1771 if (FAILED(rc))1772 rc = setError(VBOX_E_IPRT_ERROR,1773 tr("Waiting for guest process to end failed (%Rhrc)"), rc);1774 }1775 1776 return rc;1777 }1778 1779 /**1780 * Does the actual guest process execution, internal function.1781 *1782 * @return HRESULT1783 * @param aCommand Command line to execute.1784 * @param aFlags Execution flags.1785 * @param Username Username to execute the process with.1786 * @param aPassword The user's password.1787 * @param aTimeoutMS Timeout (in ms) to wait for the execution operation.1788 * @param aPID Pointer that receives the guest process' PID.1789 * @param aProgress Pointer that receives the guest process' progress object.1790 * @param pRC Pointer that receives the internal IPRT return code. Optional.1791 */1792 HRESULT Guest::executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,1793 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),1794 IN_BSTR aUsername, IN_BSTR aPassword,1795 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC)1796 {1797 /** @todo r=bird: Eventually we should clean up all the timeout parameters1798 * in the API and have the same way of specifying infinite waits! */1799 using namespace guestControl;1800 1801 AutoCaller autoCaller(this);1802 if (FAILED(autoCaller.rc())) return autoCaller.rc();1803 1804 /* Validate flags. */1805 if (aFlags != ExecuteProcessFlag_None)1806 {1807 if ( !(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses)1808 && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)1809 && !(aFlags & ExecuteProcessFlag_Hidden)1810 && !(aFlags & ExecuteProcessFlag_NoProfile)1811 && !(aFlags & ExecuteProcessFlag_WaitForStdOut)1812 && !(aFlags & ExecuteProcessFlag_WaitForStdErr))1813 {1814 if (pRC)1815 *pRC = VERR_INVALID_PARAMETER;1816 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);1817 }1818 }1819 1820 HRESULT rc = S_OK;1821 1822 try1823 {1824 /*1825 * Create progress object. Note that this is a multi operation1826 * object to perform the following steps:1827 * - Operation 1 (0): Create/start process.1828 * - Operation 2 (1): Wait for process to exit.1829 * If this progress completed successfully (S_OK), the process1830 * started and exited normally. In any other case an error/exception1831 * occurred.1832 */1833 ComObjPtr <Progress> pProgress;1834 rc = pProgress.createObject();1835 if (SUCCEEDED(rc))1836 {1837 rc = pProgress->init(static_cast<IGuest*>(this),1838 Bstr(tr("Executing process")).raw(),1839 TRUE,1840 2, /* Number of operations. */1841 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */1842 }1843 ComAssertComRC(rc);1844 1845 /*1846 * Prepare process execution.1847 */1848 int vrc = VINF_SUCCESS;1849 Utf8Str Utf8Command(aCommand);1850 1851 /* Adjust timeout. If set to 0, we define1852 * an infinite timeout. */1853 if (aTimeoutMS == 0)1854 aTimeoutMS = UINT32_MAX;1855 1856 /* Prepare arguments. */1857 char **papszArgv = NULL;1858 uint32_t uNumArgs = 0;1859 if (aArguments)1860 {1861 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));1862 uNumArgs = args.size();1863 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));1864 AssertReturn(papszArgv, E_OUTOFMEMORY);1865 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)1866 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);1867 papszArgv[uNumArgs] = NULL;1868 }1869 1870 Utf8Str Utf8UserName(aUsername);1871 Utf8Str Utf8Password(aPassword);1872 uint32_t uHostPID = 0;1873 1874 if (RT_SUCCESS(vrc))1875 {1876 uint32_t uContextID = 0;1877 1878 char *pszArgs = NULL;1879 if (uNumArgs > 0)1880 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_MS_CRT);1881 if (RT_SUCCESS(vrc))1882 {1883 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */1884 1885 /* Prepare environment. */1886 void *pvEnv = NULL;1887 uint32_t uNumEnv = 0;1888 uint32_t cbEnv = 0;1889 if (aEnvironment)1890 {1891 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));1892 1893 for (unsigned i = 0; i < env.size(); i++)1894 {1895 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);1896 if (RT_FAILURE(vrc))1897 break;1898 }1899 }1900 1901 if (RT_SUCCESS(vrc))1902 {1903 VBOXGUESTCTRL_CALLBACK callback;1904 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_START, pProgress);1905 if (RT_SUCCESS(vrc))1906 vrc = callbackAdd(&callback, &uContextID);1907 1908 if (RT_SUCCESS(vrc))1909 {1910 VBOXHGCMSVCPARM paParms[15];1911 int i = 0;1912 paParms[i++].setUInt32(uContextID);1913 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);1914 paParms[i++].setUInt32(aFlags);1915 paParms[i++].setUInt32(uNumArgs);1916 paParms[i++].setPointer((void*)pszArgs, cbArgs);1917 paParms[i++].setUInt32(uNumEnv);1918 paParms[i++].setUInt32(cbEnv);1919 paParms[i++].setPointer((void*)pvEnv, cbEnv);1920 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);1921 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);1922 1923 /*1924 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout1925 * until the process was started - the process itself then gets an infinite timeout for execution.1926 * This is handy when we want to start a process inside a worker thread within a certain timeout1927 * but let the started process perform lengthly operations then.1928 */1929 if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)1930 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);1931 else1932 paParms[i++].setUInt32(aTimeoutMS);1933 1934 VMMDev *pVMMDev = NULL;1935 {1936 /* Make sure mParent is valid, so set the read lock while using.1937 * Do not keep this lock while doing the actual call, because in the meanwhile1938 * another thread could request a write lock which would be a bad idea ... */1939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);1940 1941 /* Forward the information to the VMM device. */1942 AssertPtr(mParent);1943 pVMMDev = mParent->getVMMDev();1944 }1945 1946 if (pVMMDev)1947 {1948 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));1949 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,1950 i, paParms);1951 }1952 else1953 vrc = VERR_INVALID_VM_HANDLE;1954 }1955 RTMemFree(pvEnv);1956 }1957 RTStrFree(pszArgs);1958 }1959 1960 if (RT_SUCCESS(vrc))1961 {1962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);1963 1964 /*1965 * Generate a host-driven PID so that we immediately can return to the caller and1966 * don't need to wait until the guest started the desired process to return the1967 * PID generated by the guest OS.1968 *1969 * The guest PID will later be mapped to the host PID for later lookup.1970 */1971 vrc = VERR_NOT_FOUND; /* We did not find a host PID yet ... */1972 1973 uint32_t uTries = 0;1974 for (;;)1975 {1976 /* Create a new context ID ... */1977 uHostPID = ASMAtomicIncU32(&mNextHostPID);1978 if (uHostPID == UINT32_MAX)1979 ASMAtomicUoWriteU32(&mNextHostPID, 1000);1980 /* Is the host PID already used? Try next PID ... */1981 GuestProcessMapIter it = mGuestProcessMap.find(uHostPID);1982 if (it == mGuestProcessMap.end())1983 {1984 /* Host PID not used (anymore), we're done here ... */1985 vrc = VINF_SUCCESS;1986 break;1987 }1988 1989 if (++uTries == UINT32_MAX)1990 break; /* Don't try too hard. */1991 }1992 1993 if (RT_SUCCESS(vrc))1994 vrc = processSetStatus(uHostPID, 0 /* No guest PID yet */,1995 ExecuteProcessStatus_Undefined /* Process not started yet */,1996 0 /* uExitCode */, 0 /* uFlags */);1997 1998 if (RT_SUCCESS(vrc))1999 vrc = callbackAssignHostPID(uContextID, uHostPID);2000 }2001 else2002 rc = setErrorHGCM(vrc);2003 2004 for (unsigned i = 0; i < uNumArgs; i++)2005 RTMemFree(papszArgv[i]);2006 RTMemFree(papszArgv);2007 2008 if (RT_FAILURE(vrc))2009 rc = VBOX_E_IPRT_ERROR;2010 }2011 2012 if (SUCCEEDED(rc))2013 {2014 /* Return host PID. */2015 *aPID = uHostPID;2016 2017 /* Return the progress to the caller. */2018 pProgress.queryInterfaceTo(aProgress);2019 }2020 else2021 {2022 if (!pRC) /* Skip logging internal calls. */2023 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",2024 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));2025 }2026 2027 if (pRC)2028 *pRC = vrc;2029 }2030 catch (std::bad_alloc &)2031 {2032 rc = E_OUTOFMEMORY;2033 if (pRC)2034 *pRC = VERR_NO_MEMORY;2035 }2036 return rc;2037 }2038 #endif /* VBOX_WITH_GUEST_CONTROL */2039 2040 STDMETHODIMP Guest::SetProcessInput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten)2041 {2042 #ifndef VBOX_WITH_GUEST_CONTROL2043 ReturnComNotImplemented();2044 #else /* VBOX_WITH_GUEST_CONTROL */2045 using namespace guestControl;2046 2047 CheckComArgExpr(aPID, aPID > 0);2048 CheckComArgOutPointerValid(aBytesWritten);2049 2050 /* Validate flags. */2051 if (aFlags)2052 {2053 if (!(aFlags & ProcessInputFlag_EndOfFile))2054 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2055 }2056 2057 AutoCaller autoCaller(this);2058 if (FAILED(autoCaller.rc())) return autoCaller.rc();2059 2060 HRESULT rc = S_OK;2061 2062 try2063 {2064 VBOXGUESTCTRL_PROCESS process;2065 int vrc = processGetStatus(aPID, &process, false /* Don't remove */);2066 if (RT_SUCCESS(vrc))2067 {2068 /* PID exists; check if process is still running. */2069 if (process.mStatus != ExecuteProcessStatus_Started)2070 rc = setError(VBOX_E_IPRT_ERROR,2071 Guest::tr("Cannot inject input to a not running process (PID %u)"), aPID);2072 }2073 else2074 rc = setError(VBOX_E_IPRT_ERROR,2075 Guest::tr("Cannot inject input to a non-existent process (PID %u)"), aPID);2076 2077 if (RT_SUCCESS(vrc))2078 {2079 uint32_t uContextID = 0;2080 2081 uint32_t uGuestPID = processGetGuestPID(aPID);2082 Assert(uGuestPID);2083 2084 /*2085 * Create progress object.2086 * This progress object, compared to the one in executeProgress() above,2087 * is only single-stage local and is used to determine whether the operation2088 * finished or got canceled.2089 */2090 ComObjPtr <Progress> pProgress;2091 rc = pProgress.createObject();2092 if (SUCCEEDED(rc))2093 {2094 rc = pProgress->init(static_cast<IGuest*>(this),2095 Bstr(tr("Setting input for process")).raw(),2096 TRUE /* Cancelable */);2097 }2098 if (FAILED(rc)) throw rc;2099 ComAssert(!pProgress.isNull());2100 2101 /* Adjust timeout. */2102 if (aTimeoutMS == 0)2103 aTimeoutMS = UINT32_MAX;2104 2105 VBOXGUESTCTRL_CALLBACK callback;2106 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS, pProgress);2107 if (RT_SUCCESS(vrc))2108 {2109 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)callback.pvData;2110 2111 /* Save PID + output flags for later use. */2112 pData->u32PID = uGuestPID;2113 pData->u32Flags = aFlags;2114 }2115 2116 if (RT_SUCCESS(vrc))2117 vrc = callbackAdd(&callback, &uContextID);2118 2119 if (RT_SUCCESS(vrc))2120 {2121 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));2122 uint32_t cbSize = sfaData.size();2123 2124 VBOXHGCMSVCPARM paParms[6];2125 int i = 0;2126 paParms[i++].setUInt32(uContextID);2127 paParms[i++].setUInt32(uGuestPID);2128 paParms[i++].setUInt32(aFlags);2129 paParms[i++].setPointer(sfaData.raw(), cbSize);2130 paParms[i++].setUInt32(cbSize);2131 2132 {2133 VMMDev *pVMMDev = NULL;2134 {2135 /* Make sure mParent is valid, so set the read lock while using.2136 * Do not keep this lock while doing the actual call, because in the meanwhile2137 * another thread could request a write lock which would be a bad idea ... */2138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2139 2140 /* Forward the information to the VMM device. */2141 AssertPtr(mParent);2142 pVMMDev = mParent->getVMMDev();2143 }2144 2145 if (pVMMDev)2146 {2147 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));2148 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,2149 i, paParms);2150 if (RT_FAILURE(vrc))2151 rc = setErrorHGCM(vrc);2152 }2153 }2154 }2155 2156 if (RT_SUCCESS(vrc))2157 {2158 LogFlowFunc(("Waiting for HGCM callback ...\n"));2159 2160 /*2161 * Wait for getting back the input response from the guest.2162 */2163 vrc = callbackWaitForCompletion(uContextID, -1 /* No staging required */, aTimeoutMS);2164 if (RT_SUCCESS(vrc))2165 {2166 PCALLBACKDATAEXECINSTATUS pExecStatusIn;2167 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,2168 (void**)&pExecStatusIn, NULL /* Don't need the size. */);2169 if (RT_SUCCESS(vrc))2170 {2171 AssertPtr(pExecStatusIn);2172 switch (pExecStatusIn->u32Status)2173 {2174 case INPUT_STS_WRITTEN:2175 *aBytesWritten = pExecStatusIn->cbProcessed;2176 break;2177 2178 case INPUT_STS_ERROR:2179 rc = setError(VBOX_E_IPRT_ERROR,2180 tr("Client reported error %Rrc while processing input data"),2181 pExecStatusIn->u32Flags);2182 break;2183 2184 case INPUT_STS_TERMINATED:2185 rc = setError(VBOX_E_IPRT_ERROR,2186 tr("Client terminated while processing input data"));2187 break;2188 2189 case INPUT_STS_OVERFLOW:2190 rc = setError(VBOX_E_IPRT_ERROR,2191 tr("Client reported buffer overflow while processing input data"));2192 break;2193 2194 default:2195 /*AssertReleaseMsgFailed(("Client reported unknown input error, status=%u, flags=%u\n",2196 pExecStatusIn->u32Status, pExecStatusIn->u32Flags));*/2197 break;2198 }2199 2200 callbackFreeUserData(pExecStatusIn);2201 }2202 else2203 {2204 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2205 tr("Unable to retrieve process input status data"));2206 }2207 }2208 else2209 rc = setErrorCompletion(vrc);2210 }2211 2212 {2213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);2214 2215 /* The callback isn't needed anymore -- just was kept locally. */2216 callbackRemove(uContextID);2217 }2218 2219 /* Cleanup. */2220 if (!pProgress.isNull())2221 pProgress->uninit();2222 pProgress.setNull();2223 }2224 }2225 catch (std::bad_alloc &)2226 {2227 rc = E_OUTOFMEMORY;2228 }2229 return rc;2230 #endif2231 }2232 2233 STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))2234 {2235 #ifndef VBOX_WITH_GUEST_CONTROL2236 ReturnComNotImplemented();2237 #else /* VBOX_WITH_GUEST_CONTROL */2238 using namespace guestControl;2239 2240 return getProcessOutputInternal(aPID, aFlags, aTimeoutMS,2241 aSize, ComSafeArrayOutArg(aData), NULL /* rc */);2242 #endif2243 }2244 2245 HRESULT Guest::getProcessOutputInternal(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS,2246 LONG64 aSize, ComSafeArrayOut(BYTE, aData), int *pRC)2247 {2248 /** @todo r=bird: Eventually we should clean up all the timeout parameters2249 * in the API and have the same way of specifying infinite waits! */2250 #ifndef VBOX_WITH_GUEST_CONTROL2251 ReturnComNotImplemented();2252 #else /* VBOX_WITH_GUEST_CONTROL */2253 using namespace guestControl;2254 2255 CheckComArgExpr(aPID, aPID > 0);2256 if (aSize < 0)2257 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);2258 if (aSize == 0)2259 return setError(E_INVALIDARG, tr("The size (%lld) is zero"), aSize);2260 if (aFlags)2261 {2262 if (!(aFlags & ProcessOutputFlag_StdErr))2263 {2264 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2265 }2266 }2267 2268 AutoCaller autoCaller(this);2269 if (FAILED(autoCaller.rc())) return autoCaller.rc();2270 2271 HRESULT rc = S_OK;2272 2273 try2274 {2275 VBOXGUESTCTRL_PROCESS proc;2276 int vrc = processGetStatus(aPID, &proc, false /* Don't remove */);2277 if (RT_FAILURE(vrc))2278 {2279 rc = setError(VBOX_E_IPRT_ERROR,2280 Guest::tr("Guest process (PID %u) does not exist"), aPID);2281 }2282 else if (proc.mStatus != ExecuteProcessStatus_Started)2283 {2284 /* If the process is already or still in the process table but does not run yet2285 * (or anymore) don't remove it but report back an appropriate error. */2286 vrc = VERR_BROKEN_PIPE;2287 /* Not getting any output is fine, so don't report an API error (rc)2288 * and only signal something through internal error code (vrc). */2289 }2290 2291 if (RT_SUCCESS(vrc))2292 {2293 uint32_t uContextID = 0;2294 2295 /*2296 * Create progress object.2297 * This progress object, compared to the one in executeProgress() above,2298 * is only single-stage local and is used to determine whether the operation2299 * finished or got canceled.2300 */2301 ComObjPtr <Progress> pProgress;2302 rc = pProgress.createObject();2303 if (SUCCEEDED(rc))2304 {2305 rc = pProgress->init(static_cast<IGuest*>(this),2306 Bstr(tr("Getting output for guest process")).raw(),2307 TRUE /* Cancelable */);2308 }2309 if (FAILED(rc)) throw rc;2310 ComAssert(!pProgress.isNull());2311 2312 /* Adjust timeout. */2313 if (aTimeoutMS == 0)2314 aTimeoutMS = UINT32_MAX;2315 2316 /* Set handle ID. */2317 uint32_t uHandleID = OUTPUT_HANDLE_ID_STDOUT; /* Default */2318 if (aFlags & ProcessOutputFlag_StdErr)2319 uHandleID = OUTPUT_HANDLE_ID_STDERR;2320 2321 uint32_t uGuestPID = processGetGuestPID(aPID);2322 Assert(uGuestPID);2323 2324 /** @todo Use a buffer for next iteration if returned data is too big2325 * for current read.2326 * aSize is bogus -- will be ignored atm! */2327 VBOXGUESTCTRL_CALLBACK callback;2328 vrc = callbackInit(&callback, VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT, pProgress);2329 if (RT_SUCCESS(vrc))2330 {2331 PCALLBACKDATAEXECOUT pData = (PCALLBACKDATAEXECOUT)callback.pvData;2332 2333 /* Save PID + output flags for later use. */2334 pData->u32PID = uGuestPID;2335 pData->u32Flags = aFlags;2336 }2337 2338 if (RT_SUCCESS(vrc))2339 vrc = callbackAdd(&callback, &uContextID);2340 2341 if (RT_SUCCESS(vrc))2342 {2343 VBOXHGCMSVCPARM paParms[5];2344 int i = 0;2345 paParms[i++].setUInt32(uContextID);2346 paParms[i++].setUInt32(uGuestPID);2347 paParms[i++].setUInt32(uHandleID);2348 paParms[i++].setUInt32(0 /* Flags, none set yet */);2349 2350 VMMDev *pVMMDev = NULL;2351 {2352 /* Make sure mParent is valid, so set the read lock while using.2353 * Do not keep this lock while doing the actual call, because in the meanwhile2354 * another thread could request a write lock which would be a bad idea ... */2355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2356 2357 /* Forward the information to the VMM device. */2358 AssertPtr(mParent);2359 pVMMDev = mParent->getVMMDev();2360 }2361 2362 if (pVMMDev)2363 {2364 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));2365 vrc = pVMMDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,2366 i, paParms);2367 }2368 }2369 2370 if (RT_SUCCESS(vrc))2371 {2372 LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));2373 2374 /*2375 * Wait for the HGCM low level callback until the process2376 * has been started (or something went wrong). This is necessary to2377 * get the PID.2378 */2379 2380 /*2381 * Wait for the first output callback notification to arrive.2382 */2383 vrc = callbackWaitForCompletion(uContextID, -1 /* No staging required */, aTimeoutMS);2384 if (RT_SUCCESS(vrc))2385 {2386 PCALLBACKDATAEXECOUT pExecOut = NULL;2387 vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,2388 (void**)&pExecOut, NULL /* Don't need the size. */);2389 if (RT_SUCCESS(vrc))2390 {2391 com::SafeArray<BYTE> outputData((size_t)aSize);2392 2393 if (pExecOut->cbData)2394 {2395 bool fResize;2396 2397 /* Do we need to resize the array? */2398 if (pExecOut->cbData > aSize)2399 {2400 fResize = outputData.resize(pExecOut->cbData);2401 Assert(fResize);2402 }2403 2404 /* Fill output in supplied out buffer. */2405 Assert(outputData.size() >= pExecOut->cbData);2406 memcpy(outputData.raw(), pExecOut->pvData, pExecOut->cbData);2407 fResize = outputData.resize(pExecOut->cbData); /* Shrink to fit actual buffer size. */2408 Assert(fResize);2409 }2410 else2411 {2412 /* No data within specified timeout available. */2413 bool fResize = outputData.resize(0);2414 Assert(fResize);2415 }2416 2417 /* Detach output buffer to output argument. */2418 outputData.detachTo(ComSafeArrayOutArg(aData));2419 2420 callbackFreeUserData(pExecOut);2421 }2422 else2423 {2424 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,2425 tr("Unable to retrieve process output data (%Rrc)"), vrc);2426 }2427 }2428 else2429 rc = setErrorCompletion(vrc);2430 }2431 else2432 rc = setErrorHGCM(vrc);2433 2434 {2435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);2436 2437 /* The callback isn't needed anymore -- just was kept locally. */2438 callbackRemove(uContextID);2439 }2440 2441 /* Cleanup. */2442 if (!pProgress.isNull())2443 pProgress->uninit();2444 pProgress.setNull();2445 }2446 2447 if (pRC)2448 *pRC = vrc;2449 }2450 catch (std::bad_alloc &)2451 {2452 rc = E_OUTOFMEMORY;2453 if (pRC)2454 *pRC = VERR_NO_MEMORY;2455 }2456 return rc;2457 #endif2458 }2459 2460 STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ExecuteProcessStatus_T *aStatus)2461 {2462 #ifndef VBOX_WITH_GUEST_CONTROL2463 ReturnComNotImplemented();2464 #else /* VBOX_WITH_GUEST_CONTROL */2465 2466 AutoCaller autoCaller(this);2467 if (FAILED(autoCaller.rc())) return autoCaller.rc();2468 2469 HRESULT rc = S_OK;2470 2471 try2472 {2473 VBOXGUESTCTRL_PROCESS process;2474 int vrc = processGetStatus(aPID, &process,2475 true /* Remove when terminated */);2476 if (RT_SUCCESS(vrc))2477 {2478 if (aExitCode)2479 *aExitCode = process.mExitCode;2480 if (aFlags)2481 *aFlags = process.mFlags;2482 if (aStatus)2483 *aStatus = process.mStatus;2484 }2485 else2486 rc = setError(VBOX_E_IPRT_ERROR,2487 tr("Process (PID %u) not found!"), aPID);2488 }2489 catch (std::bad_alloc &)2490 {2491 rc = E_OUTOFMEMORY;2492 }2493 return rc;2494 #endif2495 }2496 2497 STDMETHODIMP Guest::CopyFromGuest(IN_BSTR aSource, IN_BSTR aDest,2498 IN_BSTR aUsername, IN_BSTR aPassword,2499 ULONG aFlags, IProgress **aProgress)2500 {2501 #ifndef VBOX_WITH_GUEST_CONTROL2502 ReturnComNotImplemented();2503 #else /* VBOX_WITH_GUEST_CONTROL */2504 CheckComArgStrNotEmptyOrNull(aSource);2505 CheckComArgStrNotEmptyOrNull(aDest);2506 CheckComArgOutPointerValid(aProgress);2507 2508 /* Do not allow anonymous executions (with system rights). */2509 if (RT_UNLIKELY((aUsername) == NULL || *(aUsername) == '\0'))2510 return setError(E_INVALIDARG, tr("No user name specified"));2511 2512 AutoCaller autoCaller(this);2513 if (FAILED(autoCaller.rc())) return autoCaller.rc();2514 2515 /* Validate flags. */2516 if (aFlags != CopyFileFlag_None)2517 {2518 if ( !(aFlags & CopyFileFlag_Recursive)2519 && !(aFlags & CopyFileFlag_Update)2520 && !(aFlags & CopyFileFlag_FollowLinks))2521 {2522 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2523 }2524 }2525 2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2527 2528 HRESULT rc = S_OK;2529 2530 ComObjPtr<Progress> progress;2531 try2532 {2533 /* Create the progress object. */2534 progress.createObject();2535 2536 rc = progress->init(static_cast<IGuest*>(this),2537 Bstr(tr("Copying file from guest to host")).raw(),2538 TRUE /* aCancelable */);2539 if (FAILED(rc)) throw rc;2540 2541 /* Initialize our worker task. */2542 GuestTask *pTask = new GuestTask(GuestTask::TaskType_CopyFileFromGuest, this, progress);2543 AssertPtr(pTask);2544 std::auto_ptr<GuestTask> task(pTask);2545 2546 /* Assign data - aSource is the source file on the host,2547 * aDest reflects the full path on the guest. */2548 task->strSource = (Utf8Str(aSource));2549 task->strDest = (Utf8Str(aDest));2550 task->strUserName = (Utf8Str(aUsername));2551 task->strPassword = (Utf8Str(aPassword));2552 task->uFlags = aFlags;2553 2554 rc = task->startThread();2555 if (FAILED(rc)) throw rc;2556 2557 /* Don't destruct on success. */2558 task.release();2559 }2560 catch (HRESULT aRC)2561 {2562 rc = aRC;2563 }2564 2565 if (SUCCEEDED(rc))2566 {2567 /* Return progress to the caller. */2568 progress.queryInterfaceTo(aProgress);2569 }2570 return rc;2571 #endif /* VBOX_WITH_GUEST_CONTROL */2572 }2573 2574 STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,2575 IN_BSTR aUsername, IN_BSTR aPassword,2576 ULONG aFlags, IProgress **aProgress)2577 {2578 #ifndef VBOX_WITH_GUEST_CONTROL2579 ReturnComNotImplemented();2580 #else /* VBOX_WITH_GUEST_CONTROL */2581 CheckComArgStrNotEmptyOrNull(aSource);2582 CheckComArgStrNotEmptyOrNull(aDest);2583 CheckComArgOutPointerValid(aProgress);2584 2585 /* Do not allow anonymous executions (with system rights). */2586 if (RT_UNLIKELY((aUsername) == NULL || *(aUsername) == '\0'))2587 return setError(E_INVALIDARG, tr("No user name specified"));2588 2589 AutoCaller autoCaller(this);2590 if (FAILED(autoCaller.rc())) return autoCaller.rc();2591 2592 /* Validate flags. */2593 if (aFlags != CopyFileFlag_None)2594 {2595 if ( !(aFlags & CopyFileFlag_Recursive)2596 && !(aFlags & CopyFileFlag_Update)2597 && !(aFlags & CopyFileFlag_FollowLinks))2598 {2599 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);2600 }2601 }2602 2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);2604 2605 HRESULT rc = S_OK;2606 2607 ComObjPtr<Progress> pProgress;2608 try2609 {2610 /* Create the progress object. */2611 pProgress.createObject();2612 2613 rc = pProgress->init(static_cast<IGuest*>(this),2614 Bstr(tr("Copying file from host to guest")).raw(),2615 TRUE /* aCancelable */);2616 if (FAILED(rc)) throw rc;2617 2618 /* Initialize our worker task. */2619 GuestTask *pTask = new GuestTask(GuestTask::TaskType_CopyFileToGuest, this, pProgress);2620 AssertPtr(pTask);2621 std::auto_ptr<GuestTask> task(pTask);2622 2623 /* Assign data - aSource is the source file on the host,2624 * aDest reflects the full path on the guest. */2625 task->strSource = (Utf8Str(aSource));2626 task->strDest = (Utf8Str(aDest));2627 task->strUserName = (Utf8Str(aUsername));2628 task->strPassword = (Utf8Str(aPassword));2629 task->uFlags = aFlags;2630 2631 rc = task->startThread();2632 if (FAILED(rc)) throw rc;2633 2634 /* Don't destruct on success. */2635 task.release();2636 }2637 catch (HRESULT aRC)2638 {2639 rc = aRC;2640 }2641 2642 if (SUCCEEDED(rc))2643 {2644 /* Return progress to the caller. */2645 pProgress.queryInterfaceTo(aProgress);2646 }2647 return rc;2648 #endif /* VBOX_WITH_GUEST_CONTROL */2649 }2650 131 2651 132 STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(AdditionsUpdateFlag_T, aFlags), IProgress **aProgress) … … 2676 157 2677 158 HRESULT hr = S_OK; 2678 #if 1 159 2679 160 /* Create an anonymous session. This is required to run the Guest Additions 2680 161 * update process with administrative rights. */ … … 2724 205 } 2725 206 return hr; 2726 #else /* Legacy, can be removed later. */2727 ComObjPtr<Progress> progress;2728 try2729 {2730 /* Create the progress object. */2731 progress.createObject();2732 2733 rc = progress->init(static_cast<IGuest*>(this),2734 Bstr(tr("Updating Guest Additions")).raw(),2735 TRUE /* aCancelable */);2736 if (FAILED(rc)) throw rc;2737 2738 /* Initialize our worker task. */2739 GuestTask *pTask = new GuestTask(GuestTask::TaskType_UpdateGuestAdditions, this, progress);2740 AssertPtr(pTask);2741 std::auto_ptr<GuestTask> task(pTask);2742 2743 /* Assign data - in that case aSource is the full path2744 * to the Guest Additions .ISO we want to mount. */2745 task->strSource = (Utf8Str(aSource));2746 task->uFlags = aFlags;2747 2748 rc = task->startThread();2749 if (FAILED(rc)) throw rc;2750 2751 /* Don't destruct on success. */2752 task.release();2753 }2754 catch (HRESULT aRC)2755 {2756 rc = aRC;2757 }2758 2759 if (SUCCEEDED(rc))2760 {2761 /* Return progress to the caller. */2762 progress.queryInterfaceTo(aProgress);2763 }2764 return rc;2765 #endif2766 207 #endif /* VBOX_WITH_GUEST_CONTROL */ 2767 208 } -
trunk/src/VBox/Main/src-client/GuestImpl.cpp
r42817 r42864 32 32 33 33 #include <VBox/VMMDev.h> 34 #ifdef VBOX_WITH_GUEST_CONTROL35 # include <VBox/com/array.h>36 # include <VBox/com/ErrorInfo.h>37 #endif38 34 #include <iprt/cpp/utils.h> 39 35 #include <iprt/timer.h> … … 108 104 AssertMsgRC(vrc, ("Failed to create guest statistics update timer(%Rra)\n", vrc)); 109 105 110 #ifdef VBOX_WITH_GUEST_CONTROL111 /* Init the context ID counter at 1000. */112 mNextContextID = 1000;113 /* Init the host PID counter. */114 mNextHostPID = 0;115 #endif116 117 106 #ifdef VBOX_WITH_DRAG_AND_DROP 118 107 m_pGuestDnD = new GuestDnD(this); … … 134 123 if (autoUninitSpan.uninitDone()) 135 124 return; 136 137 #ifdef VBOX_WITH_GUEST_CONTROL138 /* Scope write lock as much as possible. */139 {140 /*141 * Cleanup must be done *before* AutoUninitSpan to cancel all142 * all outstanding waits in API functions (which hold AutoCaller143 * ref counts).144 */145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);146 147 /* Notify left over callbacks that we are about to shutdown ... */148 CallbackMapIter it;149 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)150 {151 int rc2 = callbackNotifyEx(it->first, VERR_CANCELLED,152 Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));153 AssertRC(rc2);154 }155 156 /* Destroy left over callback data. */157 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)158 callbackDestroy(it->first);159 160 /* Clear process map (remove all callbacks). */161 mGuestProcessMap.clear();162 }163 #endif164 125 165 126 /* Destroy stat update timer */ -
trunk/src/VBox/Main/src-client/xpcom/module.cpp
r42852 r42864 67 67 NS_DECL_CLASSINFO(GuestDirectory) 68 68 NS_IMPL_THREADSAFE_ISUPPORTS2_CI(GuestDirectory, IGuestDirectory, IDirectory) 69 NS_DECL_CLASSINFO(GuestDirEntry)70 NS_IMPL_THREADSAFE_ISUPPORTS1_CI(GuestDirEntry, IGuestDirEntry)71 69 NS_DECL_CLASSINFO(GuestFile) 72 70 NS_IMPL_THREADSAFE_ISUPPORTS2_CI(GuestFile, IGuestFile, IFile)
Note:
See TracChangeset
for help on using the changeset viewer.