[42084] | 1 | /* $Id: GuestProcessImpl.cpp 104178 2024-04-05 12:23:48Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[44863] | 3 | * VirtualBox Main - Guest process handling.
|
---|
[42084] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2012-2023 Oracle and/or its affiliates.
|
---|
[42084] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[42084] | 26 | */
|
---|
| 27 |
|
---|
[42507] | 28 | /**
|
---|
| 29 | * Locking rules:
|
---|
| 30 | * - When the main dispatcher (callbackDispatcher) is called it takes the
|
---|
| 31 | * WriteLock while dispatching to the various on* methods.
|
---|
| 32 | * - All other outer functions (accessible by Main) must not own a lock
|
---|
| 33 | * while waiting for a callback or for an event.
|
---|
| 34 | * - Only keep Read/WriteLocks as short as possible and only when necessary.
|
---|
| 35 | */
|
---|
[42084] | 36 |
|
---|
[57358] | 37 |
|
---|
| 38 | /*********************************************************************************************************************************
|
---|
| 39 | * Header Files *
|
---|
| 40 | *********************************************************************************************************************************/
|
---|
[71173] | 41 | #define LOG_GROUP LOG_GROUP_MAIN_GUESTPROCESS
|
---|
[67914] | 42 | #include "LoggingNew.h"
|
---|
| 43 |
|
---|
[55645] | 44 | #ifndef VBOX_WITH_GUEST_CONTROL
|
---|
| 45 | # error "VBOX_WITH_GUEST_CONTROL must defined in this file"
|
---|
| 46 | #endif
|
---|
[83405] | 47 | #include "GuestImpl.h"
|
---|
[42084] | 48 | #include "GuestProcessImpl.h"
|
---|
[42160] | 49 | #include "GuestSessionImpl.h"
|
---|
[42478] | 50 | #include "GuestCtrlImplPrivate.h"
|
---|
[42160] | 51 | #include "ConsoleImpl.h"
|
---|
[45805] | 52 | #include "VirtualBoxErrorInfoImpl.h"
|
---|
[42084] | 53 |
|
---|
| 54 | #include "Global.h"
|
---|
| 55 | #include "AutoCaller.h"
|
---|
[45805] | 56 | #include "VBoxEvents.h"
|
---|
[58521] | 57 | #include "ThreadTask.h"
|
---|
[42084] | 58 |
|
---|
[42162] | 59 | #include <memory> /* For auto_ptr. */
|
---|
| 60 |
|
---|
[42160] | 61 | #include <iprt/asm.h>
|
---|
[45415] | 62 | #include <iprt/cpp/utils.h> /* For unconst(). */
|
---|
[42160] | 63 | #include <iprt/getopt.h>
|
---|
[45415] | 64 |
|
---|
[45780] | 65 | #include <VBox/com/listeners.h>
|
---|
| 66 |
|
---|
[42160] | 67 | #include <VBox/com/array.h>
|
---|
[42084] | 68 |
|
---|
[42160] | 69 |
|
---|
[92897] | 70 | /**
|
---|
| 71 | * Base class for all guest process tasks.
|
---|
| 72 | */
|
---|
[58521] | 73 | class GuestProcessTask : public ThreadTask
|
---|
[42160] | 74 | {
|
---|
[42897] | 75 | public:
|
---|
| 76 |
|
---|
[42160] | 77 | GuestProcessTask(GuestProcess *pProcess)
|
---|
[58552] | 78 | : ThreadTask("GenericGuestProcessTask")
|
---|
| 79 | , mProcess(pProcess)
|
---|
[98262] | 80 | , mVrc(VINF_SUCCESS) { }
|
---|
[42160] | 81 |
|
---|
[42436] | 82 | virtual ~GuestProcessTask(void) { }
|
---|
[42160] | 83 |
|
---|
[92897] | 84 | /** Returns the last set result code. */
|
---|
[98262] | 85 | int i_vrc(void) const { return mVrc; }
|
---|
[92897] | 86 | /** Returns whether the last set result is okay (successful) or not. */
|
---|
[98262] | 87 | bool i_isOk(void) const { return RT_SUCCESS(mVrc); }
|
---|
[92897] | 88 | /** Returns the reference of the belonging progress object. */
|
---|
[50709] | 89 | const ComObjPtr<GuestProcess> &i_process(void) const { return mProcess; }
|
---|
[42160] | 90 |
|
---|
[42897] | 91 | protected:
|
---|
| 92 |
|
---|
[92897] | 93 | /** Progress object this process belongs to. */
|
---|
[42160] | 94 | const ComObjPtr<GuestProcess> mProcess;
|
---|
[92897] | 95 | /** Last set result code. */
|
---|
[98262] | 96 | int mVrc;
|
---|
[42160] | 97 | };
|
---|
| 98 |
|
---|
[92897] | 99 | /**
|
---|
| 100 | * Task to start a process on the guest.
|
---|
| 101 | */
|
---|
[42897] | 102 | class GuestProcessStartTask : public GuestProcessTask
|
---|
[42160] | 103 | {
|
---|
[42897] | 104 | public:
|
---|
| 105 |
|
---|
[42160] | 106 | GuestProcessStartTask(GuestProcess *pProcess)
|
---|
[58521] | 107 | : GuestProcessTask(pProcess)
|
---|
| 108 | {
|
---|
| 109 | m_strTaskName = "gctlPrcStart";
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 | void handler()
|
---|
| 113 | {
|
---|
[63186] | 114 | GuestProcess::i_startProcessThreadTask(this);
|
---|
[58521] | 115 | }
|
---|
[42160] | 116 | };
|
---|
| 117 |
|
---|
[45780] | 118 | /**
|
---|
| 119 | * Internal listener class to serve events in an
|
---|
| 120 | * active manner, e.g. without polling delays.
|
---|
| 121 | */
|
---|
| 122 | class GuestProcessListener
|
---|
| 123 | {
|
---|
| 124 | public:
|
---|
| 125 |
|
---|
| 126 | GuestProcessListener(void)
|
---|
| 127 | {
|
---|
| 128 | }
|
---|
| 129 |
|
---|
[62157] | 130 | virtual ~GuestProcessListener(void)
|
---|
| 131 | {
|
---|
| 132 | }
|
---|
| 133 |
|
---|
[45780] | 134 | HRESULT init(GuestProcess *pProcess)
|
---|
| 135 | {
|
---|
[49610] | 136 | AssertPtrReturn(pProcess, E_POINTER);
|
---|
[45780] | 137 | mProcess = pProcess;
|
---|
| 138 | return S_OK;
|
---|
| 139 | }
|
---|
| 140 |
|
---|
| 141 | void uninit(void)
|
---|
| 142 | {
|
---|
[49610] | 143 | mProcess = NULL;
|
---|
[45780] | 144 | }
|
---|
| 145 |
|
---|
| 146 | STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
|
---|
| 147 | {
|
---|
| 148 | switch (aType)
|
---|
| 149 | {
|
---|
| 150 | case VBoxEventType_OnGuestProcessStateChanged:
|
---|
| 151 | case VBoxEventType_OnGuestProcessInputNotify:
|
---|
| 152 | case VBoxEventType_OnGuestProcessOutput:
|
---|
| 153 | {
|
---|
[49610] | 154 | AssertPtrReturn(mProcess, E_POINTER);
|
---|
[94915] | 155 | int vrc2 = mProcess->signalWaitEvent(aType, aEvent);
|
---|
| 156 | RT_NOREF(vrc2);
|
---|
[63248] | 157 | #ifdef LOG_ENABLED
|
---|
[94915] | 158 | LogFlowThisFunc(("Signalling events of type=%RU32, pProcess=%p resulted in vrc=%Rrc\n",
|
---|
| 159 | aType, &mProcess, vrc2));
|
---|
[45780] | 160 | #endif
|
---|
| 161 | break;
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | default:
|
---|
[49504] | 165 | AssertMsgFailed(("Unhandled event %RU32\n", aType));
|
---|
[45780] | 166 | break;
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | return S_OK;
|
---|
| 170 | }
|
---|
| 171 |
|
---|
| 172 | private:
|
---|
| 173 |
|
---|
[49610] | 174 | GuestProcess *mProcess;
|
---|
[45780] | 175 | };
|
---|
| 176 | typedef ListenerImpl<GuestProcessListener, GuestProcess*> GuestProcessListenerImpl;
|
---|
| 177 |
|
---|
| 178 | VBOX_LISTENER_DECLARE(GuestProcessListenerImpl)
|
---|
| 179 |
|
---|
[42084] | 180 | // constructor / destructor
|
---|
| 181 | /////////////////////////////////////////////////////////////////////////////
|
---|
| 182 |
|
---|
| 183 | DEFINE_EMPTY_CTOR_DTOR(GuestProcess)
|
---|
| 184 |
|
---|
| 185 | HRESULT GuestProcess::FinalConstruct(void)
|
---|
| 186 | {
|
---|
[42354] | 187 | LogFlowThisFuncEnter();
|
---|
[45415] | 188 | return BaseFinalConstruct();
|
---|
[42084] | 189 | }
|
---|
| 190 |
|
---|
| 191 | void GuestProcess::FinalRelease(void)
|
---|
| 192 | {
|
---|
| 193 | LogFlowThisFuncEnter();
|
---|
| 194 | uninit();
|
---|
| 195 | BaseFinalRelease();
|
---|
| 196 | LogFlowThisFuncLeave();
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 | // public initializer/uninitializer for internal purposes only
|
---|
| 200 | /////////////////////////////////////////////////////////////////////////////
|
---|
| 201 |
|
---|
[92897] | 202 | /**
|
---|
| 203 | * Initialies a guest process object.
|
---|
| 204 | *
|
---|
| 205 | * @returns VBox status code.
|
---|
| 206 | * @param aConsole Console this process is bound to.
|
---|
| 207 | * @param aSession Guest session this process is bound to.
|
---|
| 208 | * @param aObjectID Object ID to use for this process object.
|
---|
| 209 | * @param aProcInfo Process startup information to use.
|
---|
| 210 | * @param pBaseEnv Guest environment to apply when starting the process on the guest.
|
---|
| 211 | */
|
---|
[71406] | 212 | int GuestProcess::init(Console *aConsole, GuestSession *aSession, ULONG aObjectID,
|
---|
[55591] | 213 | const GuestProcessStartupInfo &aProcInfo, const GuestEnvironment *pBaseEnv)
|
---|
[42084] | 214 | {
|
---|
[71406] | 215 | LogFlowThisFunc(("aConsole=%p, aSession=%p, aObjectID=%RU32, pBaseEnv=%p\n",
|
---|
| 216 | aConsole, aSession, aObjectID, pBaseEnv));
|
---|
[42436] | 217 |
|
---|
[44863] | 218 | AssertPtrReturn(aConsole, VERR_INVALID_POINTER);
|
---|
[42160] | 219 | AssertPtrReturn(aSession, VERR_INVALID_POINTER);
|
---|
| 220 |
|
---|
[42084] | 221 | /* Enclose the state transition NotReady->InInit->Ready. */
|
---|
| 222 | AutoInitSpan autoInitSpan(this);
|
---|
[42105] | 223 | AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
|
---|
[42084] | 224 |
|
---|
[98272] | 225 | HRESULT hrc;
|
---|
[45780] | 226 |
|
---|
[71406] | 227 | int vrc = bindToSession(aConsole, aSession, aObjectID);
|
---|
[44863] | 228 | if (RT_SUCCESS(vrc))
|
---|
| 229 | {
|
---|
[98272] | 230 | hrc = unconst(mEventSource).createObject();
|
---|
| 231 | if (FAILED(hrc))
|
---|
[47469] | 232 | vrc = VERR_NO_MEMORY;
|
---|
| 233 | else
|
---|
| 234 | {
|
---|
[98272] | 235 | hrc = mEventSource->init();
|
---|
| 236 | if (FAILED(hrc))
|
---|
[47469] | 237 | vrc = VERR_COM_UNEXPECTED;
|
---|
| 238 | }
|
---|
[45415] | 239 | }
|
---|
| 240 |
|
---|
| 241 | if (RT_SUCCESS(vrc))
|
---|
| 242 | {
|
---|
[45780] | 243 | try
|
---|
| 244 | {
|
---|
| 245 | GuestProcessListener *pListener = new GuestProcessListener();
|
---|
| 246 | ComObjPtr<GuestProcessListenerImpl> thisListener;
|
---|
[98272] | 247 | hrc = thisListener.createObject();
|
---|
| 248 | if (SUCCEEDED(hrc))
|
---|
| 249 | hrc = thisListener->init(pListener, this);
|
---|
[45780] | 250 |
|
---|
[98272] | 251 | if (SUCCEEDED(hrc))
|
---|
[45780] | 252 | {
|
---|
| 253 | com::SafeArray <VBoxEventType_T> eventTypes;
|
---|
| 254 | eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
|
---|
| 255 | eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
|
---|
| 256 | eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
|
---|
[98272] | 257 | hrc = mEventSource->RegisterListener(thisListener,
|
---|
[45780] | 258 | ComSafeArrayAsInParam(eventTypes),
|
---|
| 259 | TRUE /* Active listener */);
|
---|
[98272] | 260 | if (SUCCEEDED(hrc))
|
---|
[45780] | 261 | {
|
---|
[47469] | 262 | vrc = baseInit();
|
---|
| 263 | if (RT_SUCCESS(vrc))
|
---|
| 264 | {
|
---|
| 265 | mLocalListener = thisListener;
|
---|
| 266 | }
|
---|
[45780] | 267 | }
|
---|
| 268 | else
|
---|
| 269 | vrc = VERR_COM_UNEXPECTED;
|
---|
| 270 | }
|
---|
| 271 | else
|
---|
| 272 | vrc = VERR_COM_UNEXPECTED;
|
---|
| 273 | }
|
---|
| 274 | catch(std::bad_alloc &)
|
---|
| 275 | {
|
---|
| 276 | vrc = VERR_NO_MEMORY;
|
---|
| 277 | }
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | if (RT_SUCCESS(vrc))
|
---|
| 281 | {
|
---|
[44863] | 282 | mData.mProcess = aProcInfo;
|
---|
[55591] | 283 | mData.mpSessionBaseEnv = pBaseEnv;
|
---|
| 284 | if (pBaseEnv)
|
---|
| 285 | pBaseEnv->retainConst();
|
---|
[45426] | 286 | mData.mExitCode = 0;
|
---|
| 287 | mData.mPID = 0;
|
---|
[47817] | 288 | mData.mLastError = VINF_SUCCESS;
|
---|
[45426] | 289 | mData.mStatus = ProcessStatus_Undefined;
|
---|
[44863] | 290 | /* Everything else will be set by the actual starting routine. */
|
---|
[42105] | 291 |
|
---|
[44863] | 292 | /* Confirm a successful initialization when it's the case. */
|
---|
| 293 | autoInitSpan.setSucceeded();
|
---|
[42234] | 294 |
|
---|
[44863] | 295 | return vrc;
|
---|
| 296 | }
|
---|
| 297 |
|
---|
| 298 | autoInitSpan.setFailed();
|
---|
| 299 | return vrc;
|
---|
[42084] | 300 | }
|
---|
| 301 |
|
---|
| 302 | /**
|
---|
| 303 | * Uninitializes the instance.
|
---|
[49504] | 304 | * Called from FinalRelease() or IGuestSession::uninit().
|
---|
[42084] | 305 | */
|
---|
| 306 | void GuestProcess::uninit(void)
|
---|
| 307 | {
|
---|
| 308 | /* Enclose the state transition Ready->InUninit->NotReady. */
|
---|
| 309 | AutoUninitSpan autoUninitSpan(this);
|
---|
| 310 | if (autoUninitSpan.uninitDone())
|
---|
| 311 | return;
|
---|
[42234] | 312 |
|
---|
[55535] | 313 | LogFlowThisFunc(("mExe=%s, PID=%RU32\n", mData.mProcess.mExecutable.c_str(), mData.mPID));
|
---|
[42897] | 314 |
|
---|
[55591] | 315 | if (mData.mpSessionBaseEnv)
|
---|
| 316 | {
|
---|
| 317 | mData.mpSessionBaseEnv->releaseConst();
|
---|
| 318 | mData.mpSessionBaseEnv = NULL;
|
---|
| 319 | }
|
---|
| 320 |
|
---|
[47469] | 321 | baseUninit();
|
---|
| 322 |
|
---|
[77587] | 323 | LogFlowFuncLeave();
|
---|
[42084] | 324 | }
|
---|
| 325 |
|
---|
| 326 | // implementation of public getters/setters for attributes
|
---|
| 327 | /////////////////////////////////////////////////////////////////////////////
|
---|
[50709] | 328 | HRESULT GuestProcess::getArguments(std::vector<com::Utf8Str> &aArguments)
|
---|
[42084] | 329 | {
|
---|
[42214] | 330 | LogFlowThisFuncEnter();
|
---|
| 331 |
|
---|
[42160] | 332 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[52934] | 333 | aArguments = mData.mProcess.mArguments;
|
---|
[42160] | 334 | return S_OK;
|
---|
[42084] | 335 | }
|
---|
| 336 |
|
---|
[50709] | 337 | HRESULT GuestProcess::getEnvironment(std::vector<com::Utf8Str> &aEnvironment)
|
---|
[42084] | 338 | {
|
---|
[63245] | 339 | #ifndef VBOX_WITH_GUEST_CONTROL
|
---|
[42084] | 340 | ReturnComNotImplemented();
|
---|
| 341 | #else
|
---|
[55591] | 342 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); /* (Paranoia since both environment objects are immutable.) */
|
---|
| 343 | HRESULT hrc;
|
---|
| 344 | if (mData.mpSessionBaseEnv)
|
---|
| 345 | {
|
---|
| 346 | int vrc;
|
---|
| 347 | if (mData.mProcess.mEnvironmentChanges.count() == 0)
|
---|
| 348 | vrc = mData.mpSessionBaseEnv->queryPutEnvArray(&aEnvironment);
|
---|
| 349 | else
|
---|
| 350 | {
|
---|
| 351 | GuestEnvironment TmpEnv;
|
---|
| 352 | vrc = TmpEnv.copy(*mData.mpSessionBaseEnv);
|
---|
| 353 | if (RT_SUCCESS(vrc))
|
---|
| 354 | {
|
---|
| 355 | vrc = TmpEnv.applyChanges(mData.mProcess.mEnvironmentChanges);
|
---|
[63246] | 356 | if (RT_SUCCESS(vrc))
|
---|
[55591] | 357 | vrc = TmpEnv.queryPutEnvArray(&aEnvironment);
|
---|
| 358 | }
|
---|
| 359 | }
|
---|
| 360 | hrc = Global::vboxStatusCodeToCOM(vrc);
|
---|
| 361 | }
|
---|
| 362 | else
|
---|
[71406] | 363 | hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by installed Guest Additions"));
|
---|
[55591] | 364 | LogFlowThisFuncLeave();
|
---|
| 365 | return hrc;
|
---|
| 366 | #endif
|
---|
[42084] | 367 | }
|
---|
| 368 |
|
---|
[50709] | 369 | HRESULT GuestProcess::getEventSource(ComPtr<IEventSource> &aEventSource)
|
---|
[45426] | 370 | {
|
---|
| 371 | LogFlowThisFuncEnter();
|
---|
| 372 |
|
---|
| 373 | // no need to lock - lifetime constant
|
---|
[50709] | 374 | mEventSource.queryInterfaceTo(aEventSource.asOutParam());
|
---|
[45426] | 375 |
|
---|
[47627] | 376 | LogFlowThisFuncLeave();
|
---|
[45426] | 377 | return S_OK;
|
---|
| 378 | }
|
---|
| 379 |
|
---|
[50709] | 380 | HRESULT GuestProcess::getExecutablePath(com::Utf8Str &aExecutablePath)
|
---|
[42084] | 381 | {
|
---|
[42214] | 382 | LogFlowThisFuncEnter();
|
---|
| 383 |
|
---|
[42160] | 384 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 385 |
|
---|
[55535] | 386 | aExecutablePath = mData.mProcess.mExecutable;
|
---|
[42160] | 387 |
|
---|
| 388 | return S_OK;
|
---|
[42084] | 389 | }
|
---|
| 390 |
|
---|
[50709] | 391 | HRESULT GuestProcess::getExitCode(LONG *aExitCode)
|
---|
[42084] | 392 | {
|
---|
[42214] | 393 | LogFlowThisFuncEnter();
|
---|
| 394 |
|
---|
[42160] | 395 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 396 |
|
---|
| 397 | *aExitCode = mData.mExitCode;
|
---|
| 398 |
|
---|
| 399 | return S_OK;
|
---|
[42084] | 400 | }
|
---|
| 401 |
|
---|
[50709] | 402 | HRESULT GuestProcess::getName(com::Utf8Str &aName)
|
---|
[42411] | 403 | {
|
---|
| 404 | LogFlowThisFuncEnter();
|
---|
| 405 |
|
---|
| 406 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 407 |
|
---|
[50709] | 408 | aName = mData.mProcess.mName;
|
---|
[42411] | 409 |
|
---|
| 410 | return S_OK;
|
---|
| 411 | }
|
---|
| 412 |
|
---|
[50709] | 413 | HRESULT GuestProcess::getPID(ULONG *aPID)
|
---|
[42084] | 414 | {
|
---|
[42214] | 415 | LogFlowThisFuncEnter();
|
---|
| 416 |
|
---|
[42160] | 417 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 418 |
|
---|
| 419 | *aPID = mData.mPID;
|
---|
| 420 |
|
---|
| 421 | return S_OK;
|
---|
[42084] | 422 | }
|
---|
| 423 |
|
---|
[50709] | 424 | HRESULT GuestProcess::getStatus(ProcessStatus_T *aStatus)
|
---|
[42084] | 425 | {
|
---|
[42214] | 426 | LogFlowThisFuncEnter();
|
---|
| 427 |
|
---|
[93720] | 428 | *aStatus = i_getStatus();
|
---|
[42160] | 429 |
|
---|
| 430 | return S_OK;
|
---|
[42084] | 431 | }
|
---|
| 432 |
|
---|
[42160] | 433 | // private methods
|
---|
| 434 | /////////////////////////////////////////////////////////////////////////////
|
---|
| 435 |
|
---|
[92897] | 436 | /**
|
---|
| 437 | * Entry point for guest side process callbacks.
|
---|
| 438 | *
|
---|
| 439 | * @returns VBox status code.
|
---|
| 440 | * @param pCbCtx Host callback context.
|
---|
| 441 | * @param pSvcCb Host callback data.
|
---|
| 442 | */
|
---|
[50618] | 443 | int GuestProcess::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
|
---|
[42160] | 444 | {
|
---|
[44863] | 445 | AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
|
---|
| 446 | AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
|
---|
[42439] | 447 | #ifdef DEBUG
|
---|
[76958] | 448 | LogFlowThisFunc(("uPID=%RU32, uContextID=%RU32, uMessage=%RU32, pSvcCb=%p\n",
|
---|
| 449 | mData.mPID, pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
|
---|
[42439] | 450 | #endif
|
---|
[42272] | 451 |
|
---|
[44863] | 452 | int vrc;
|
---|
[76958] | 453 | switch (pCbCtx->uMessage)
|
---|
[42461] | 454 | {
|
---|
[76958] | 455 | case GUEST_MSG_DISCONNECTED:
|
---|
[42214] | 456 | {
|
---|
[50709] | 457 | vrc = i_onGuestDisconnected(pCbCtx, pSvcCb);
|
---|
[42461] | 458 | break;
|
---|
| 459 | }
|
---|
[42214] | 460 |
|
---|
[76958] | 461 | case GUEST_MSG_EXEC_STATUS:
|
---|
[42461] | 462 | {
|
---|
[50709] | 463 | vrc = i_onProcessStatusChange(pCbCtx, pSvcCb);
|
---|
[42461] | 464 | break;
|
---|
| 465 | }
|
---|
[42214] | 466 |
|
---|
[76958] | 467 | case GUEST_MSG_EXEC_OUTPUT:
|
---|
[42461] | 468 | {
|
---|
[50709] | 469 | vrc = i_onProcessOutput(pCbCtx, pSvcCb);
|
---|
[42461] | 470 | break;
|
---|
| 471 | }
|
---|
[42214] | 472 |
|
---|
[76958] | 473 | case GUEST_MSG_EXEC_INPUT_STATUS:
|
---|
[42461] | 474 | {
|
---|
[50709] | 475 | vrc = i_onProcessInputStatus(pCbCtx, pSvcCb);
|
---|
[42461] | 476 | break;
|
---|
| 477 | }
|
---|
[42214] | 478 |
|
---|
[42461] | 479 | default:
|
---|
| 480 | /* Silently ignore not implemented functions. */
|
---|
[44863] | 481 | vrc = VERR_NOT_SUPPORTED;
|
---|
[42461] | 482 | break;
|
---|
[42214] | 483 | }
|
---|
| 484 |
|
---|
[42439] | 485 | #ifdef DEBUG
|
---|
[49349] | 486 | LogFlowFuncLeaveRC(vrc);
|
---|
[42439] | 487 | #endif
|
---|
[42721] | 488 | return vrc;
|
---|
[42214] | 489 | }
|
---|
| 490 |
|
---|
[42673] | 491 | /**
|
---|
| 492 | * Checks if the current assigned PID matches another PID (from a callback).
|
---|
| 493 | *
|
---|
| 494 | * In protocol v1 we don't have the possibility to terminate/kill
|
---|
[44935] | 495 | * processes so it can happen that a formerly started process A
|
---|
[42673] | 496 | * (which has the context ID 0 (session=0, process=0, count=0) will
|
---|
| 497 | * send a delayed message to the host if this process has already
|
---|
| 498 | * been discarded there and the same context ID was reused by
|
---|
| 499 | * a process B. Process B in turn then has a different guest PID.
|
---|
| 500 | *
|
---|
[49570] | 501 | * Note: This also can happen when restoring from a saved state which
|
---|
| 502 | * had a guest process running.
|
---|
| 503 | *
|
---|
[42673] | 504 | * @return IPRT status code.
|
---|
| 505 | * @param uPID PID to check.
|
---|
| 506 | */
|
---|
[50709] | 507 | inline int GuestProcess::i_checkPID(uint32_t uPID)
|
---|
[42411] | 508 | {
|
---|
[94915] | 509 | int vrc = VINF_SUCCESS;
|
---|
[49570] | 510 |
|
---|
[42673] | 511 | /* Was there a PID assigned yet? */
|
---|
| 512 | if (mData.mPID)
|
---|
| 513 | {
|
---|
[49570] | 514 | if (RT_UNLIKELY(mData.mPID != uPID))
|
---|
[42673] | 515 | {
|
---|
[50899] | 516 | LogFlowFunc(("Stale guest process (PID=%RU32) sent data to a newly started process (pProcesS=%p, PID=%RU32, status=%RU32)\n",
|
---|
| 517 | uPID, this, mData.mPID, mData.mStatus));
|
---|
[94915] | 518 | vrc = VERR_NOT_FOUND;
|
---|
[42673] | 519 | }
|
---|
| 520 | }
|
---|
| 521 |
|
---|
[94915] | 522 | return vrc;
|
---|
[42411] | 523 | }
|
---|
| 524 |
|
---|
[84648] | 525 | /**
|
---|
[93720] | 526 | * Returns the current process status.
|
---|
| 527 | *
|
---|
| 528 | * @returns Current process status.
|
---|
| 529 | *
|
---|
| 530 | * @note Takes the read lock.
|
---|
| 531 | */
|
---|
| 532 | ProcessStatus_T GuestProcess::i_getStatus(void)
|
---|
| 533 | {
|
---|
| 534 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 535 |
|
---|
| 536 | return mData.mStatus;
|
---|
| 537 | }
|
---|
| 538 |
|
---|
| 539 | /**
|
---|
[84648] | 540 | * Converts a given guest process error to a string.
|
---|
| 541 | *
|
---|
| 542 | * @returns Error as a string.
|
---|
[98272] | 543 | * @param vrcGuest Guest process error to return string for.
|
---|
| 544 | * @param pcszWhat Hint of what was involved when the error occurred.
|
---|
[84648] | 545 | */
|
---|
[43162] | 546 | /* static */
|
---|
[98272] | 547 | Utf8Str GuestProcess::i_guestErrorToString(int vrcGuest, const char *pcszWhat)
|
---|
[43162] | 548 | {
|
---|
[84648] | 549 | AssertPtrReturn(pcszWhat, "");
|
---|
[43162] | 550 |
|
---|
[84648] | 551 | Utf8Str strErr;
|
---|
[98272] | 552 | switch (vrcGuest)
|
---|
[91518] | 553 | {
|
---|
[84865] | 554 | #define CASE_MSG(a_iRc, ...) \
|
---|
[91518] | 555 | case a_iRc: strErr.printf(__VA_ARGS__); break;
|
---|
[84648] | 556 |
|
---|
| 557 | CASE_MSG(VERR_FILE_NOT_FOUND, tr("No such file or directory \"%s\" on guest"), pcszWhat); /* This is the most likely error. */
|
---|
| 558 | CASE_MSG(VERR_PATH_NOT_FOUND, tr("No such file or directory \"%s\" on guest"), pcszWhat);
|
---|
| 559 | CASE_MSG(VERR_INVALID_VM_HANDLE, tr("VMM device is not available (is the VM running?)"));
|
---|
| 560 | CASE_MSG(VERR_HGCM_SERVICE_NOT_FOUND, tr("The guest execution service is not available"));
|
---|
| 561 | CASE_MSG(VERR_BAD_EXE_FORMAT, tr("The file \"%s\" is not an executable format on guest"), pcszWhat);
|
---|
| 562 | CASE_MSG(VERR_AUTHENTICATION_FAILURE, tr("The user \"%s\" was not able to logon on guest"), pcszWhat);
|
---|
| 563 | CASE_MSG(VERR_INVALID_NAME, tr("The file \"%s\" is an invalid name"), pcszWhat);
|
---|
| 564 | CASE_MSG(VERR_TIMEOUT, tr("The guest did not respond within time"));
|
---|
| 565 | CASE_MSG(VERR_CANCELLED, tr("The execution operation for \"%s\" was canceled"), pcszWhat);
|
---|
| 566 | CASE_MSG(VERR_GSTCTL_MAX_CID_OBJECTS_REACHED, tr("Maximum number of concurrent guest processes has been reached"));
|
---|
| 567 | CASE_MSG(VERR_NOT_FOUND, tr("The guest execution service is not ready (yet)"));
|
---|
[43162] | 568 | default:
|
---|
[98272] | 569 | strErr.printf(tr("Error %Rrc for guest process \"%s\" occurred\n"), vrcGuest, pcszWhat);
|
---|
[43162] | 570 | break;
|
---|
[91518] | 571 | #undef CASE_MSG
|
---|
[43162] | 572 | }
|
---|
| 573 |
|
---|
[84648] | 574 | return strErr;
|
---|
[43162] | 575 | }
|
---|
| 576 |
|
---|
[61792] | 577 | /**
|
---|
[93720] | 578 | * Translates a process status to a human readable string.
|
---|
| 579 | *
|
---|
| 580 | * @returns Process status as a string.
|
---|
| 581 | * @param enmStatus Guest process status to return string for.
|
---|
| 582 | */
|
---|
| 583 | /* static */
|
---|
| 584 | Utf8Str GuestProcess::i_statusToString(ProcessStatus_T enmStatus)
|
---|
| 585 | {
|
---|
| 586 | switch (enmStatus)
|
---|
| 587 | {
|
---|
| 588 | case ProcessStatus_Starting:
|
---|
| 589 | return "starting";
|
---|
| 590 | case ProcessStatus_Started:
|
---|
| 591 | return "started";
|
---|
| 592 | case ProcessStatus_Paused:
|
---|
| 593 | return "paused";
|
---|
| 594 | case ProcessStatus_Terminating:
|
---|
| 595 | return "terminating";
|
---|
| 596 | case ProcessStatus_TerminatedNormally:
|
---|
| 597 | return "successfully terminated";
|
---|
| 598 | case ProcessStatus_TerminatedSignal:
|
---|
| 599 | return "terminated by signal";
|
---|
| 600 | case ProcessStatus_TerminatedAbnormally:
|
---|
| 601 | return "abnormally aborted";
|
---|
| 602 | case ProcessStatus_TimedOutKilled:
|
---|
| 603 | return "timed out";
|
---|
| 604 | case ProcessStatus_TimedOutAbnormally:
|
---|
| 605 | return "timed out, hanging";
|
---|
| 606 | case ProcessStatus_Down:
|
---|
| 607 | return "killed";
|
---|
| 608 | case ProcessStatus_Error:
|
---|
| 609 | return "error";
|
---|
| 610 | default:
|
---|
| 611 | break;
|
---|
| 612 | }
|
---|
| 613 |
|
---|
| 614 | AssertFailed(); /* Should never happen! */
|
---|
| 615 | return "unknown";
|
---|
| 616 | }
|
---|
| 617 |
|
---|
| 618 | /**
|
---|
[65088] | 619 | * Returns @c true if the passed in error code indicates an error which came
|
---|
| 620 | * from the guest side, or @c false if not.
|
---|
[61792] | 621 | *
|
---|
[65088] | 622 | * @return bool @c true if the passed in error code indicates an error which came
|
---|
| 623 | * from the guest side, or @c false if not.
|
---|
[98272] | 624 | * @param vrc Error code to check.
|
---|
[61792] | 625 | */
|
---|
| 626 | /* static */
|
---|
[98272] | 627 | bool GuestProcess::i_isGuestError(int vrc)
|
---|
[61792] | 628 | {
|
---|
[98272] | 629 | return vrc == VERR_GSTCTL_GUEST_ERROR
|
---|
| 630 | || vrc == VERR_GSTCTL_PROCESS_EXIT_CODE;
|
---|
[61792] | 631 | }
|
---|
| 632 |
|
---|
[92897] | 633 | /**
|
---|
| 634 | * Returns whether the guest process is alive (i.e. running) or not.
|
---|
| 635 | *
|
---|
| 636 | * @returns \c true if alive and running, or \c false if not.
|
---|
| 637 | */
|
---|
[50709] | 638 | inline bool GuestProcess::i_isAlive(void)
|
---|
[42673] | 639 | {
|
---|
[98272] | 640 | return mData.mStatus == ProcessStatus_Started
|
---|
| 641 | || mData.mStatus == ProcessStatus_Paused
|
---|
| 642 | || mData.mStatus == ProcessStatus_Terminating;
|
---|
[42673] | 643 | }
|
---|
| 644 |
|
---|
[92897] | 645 | /**
|
---|
| 646 | * Returns whether the guest process has ended (i.e. terminated) or not.
|
---|
| 647 | *
|
---|
| 648 | * @returns \c true if ended, or \c false if not.
|
---|
| 649 | */
|
---|
[50709] | 650 | inline bool GuestProcess::i_hasEnded(void)
|
---|
[42160] | 651 | {
|
---|
[98272] | 652 | return mData.mStatus == ProcessStatus_TerminatedNormally
|
---|
| 653 | || mData.mStatus == ProcessStatus_TerminatedSignal
|
---|
| 654 | || mData.mStatus == ProcessStatus_TerminatedAbnormally
|
---|
| 655 | || mData.mStatus == ProcessStatus_TimedOutKilled
|
---|
| 656 | || mData.mStatus == ProcessStatus_TimedOutAbnormally
|
---|
| 657 | || mData.mStatus == ProcessStatus_Down
|
---|
| 658 | || mData.mStatus == ProcessStatus_Error;
|
---|
[42160] | 659 | }
|
---|
| 660 |
|
---|
[92897] | 661 | /**
|
---|
| 662 | * Called when the guest side of the process has been disconnected (closed, terminated, +++).
|
---|
| 663 | *
|
---|
| 664 | * @returns VBox status code.
|
---|
| 665 | * @param pCbCtx Host callback context.
|
---|
| 666 | * @param pSvcCbData Host callback data.
|
---|
| 667 | */
|
---|
[50709] | 668 | int GuestProcess::i_onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
|
---|
[42160] | 669 | {
|
---|
[45415] | 670 | AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
|
---|
| 671 | AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
|
---|
[42354] | 672 |
|
---|
[50709] | 673 | int vrc = i_setProcessStatus(ProcessStatus_Down, VINF_SUCCESS);
|
---|
[42354] | 674 |
|
---|
[49349] | 675 | LogFlowFuncLeaveRC(vrc);
|
---|
[42721] | 676 | return vrc;
|
---|
[42160] | 677 | }
|
---|
| 678 |
|
---|
[92897] | 679 | /**
|
---|
| 680 | * Sets (reports) the current input status of the guest process.
|
---|
| 681 | *
|
---|
| 682 | * @returns VBox status code.
|
---|
| 683 | * @param pCbCtx Host callback context.
|
---|
| 684 | * @param pSvcCbData Host callback data.
|
---|
| 685 | *
|
---|
| 686 | * @note Takes the write lock.
|
---|
| 687 | */
|
---|
[50709] | 688 | int GuestProcess::i_onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
|
---|
[42214] | 689 | {
|
---|
[45415] | 690 | AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
|
---|
[44863] | 691 | AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
|
---|
[45415] | 692 | /* pCallback is optional. */
|
---|
[42354] | 693 |
|
---|
[44863] | 694 | if (pSvcCbData->mParms < 5)
|
---|
| 695 | return VERR_INVALID_PARAMETER;
|
---|
[42214] | 696 |
|
---|
[44863] | 697 | CALLBACKDATA_PROC_INPUT dataCb;
|
---|
| 698 | /* pSvcCb->mpaParms[0] always contains the context ID. */
|
---|
[75737] | 699 | int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
|
---|
[44945] | 700 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 701 | vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uStatus);
|
---|
[44945] | 702 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 703 | vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
|
---|
[44945] | 704 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 705 | vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[4], &dataCb.uProcessed);
|
---|
[44945] | 706 | AssertRCReturn(vrc, vrc);
|
---|
[44863] | 707 |
|
---|
[45415] | 708 | LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RI32, cbProcessed=%RU32\n",
|
---|
| 709 | dataCb.uPID, dataCb.uStatus, dataCb.uFlags, dataCb.uProcessed));
|
---|
[44863] | 710 |
|
---|
[50709] | 711 | vrc = i_checkPID(dataCb.uPID);
|
---|
[47469] | 712 | if (RT_SUCCESS(vrc))
|
---|
[42634] | 713 | {
|
---|
[47469] | 714 | ProcessInputStatus_T inputStatus = ProcessInputStatus_Undefined;
|
---|
| 715 | switch (dataCb.uStatus)
|
---|
| 716 | {
|
---|
| 717 | case INPUT_STS_WRITTEN:
|
---|
| 718 | inputStatus = ProcessInputStatus_Written;
|
---|
| 719 | break;
|
---|
| 720 | case INPUT_STS_ERROR:
|
---|
| 721 | inputStatus = ProcessInputStatus_Broken;
|
---|
| 722 | break;
|
---|
| 723 | case INPUT_STS_TERMINATED:
|
---|
| 724 | inputStatus = ProcessInputStatus_Broken;
|
---|
| 725 | break;
|
---|
| 726 | case INPUT_STS_OVERFLOW:
|
---|
| 727 | inputStatus = ProcessInputStatus_Overflow;
|
---|
| 728 | break;
|
---|
| 729 | case INPUT_STS_UNDEFINED:
|
---|
| 730 | /* Fall through is intentional. */
|
---|
| 731 | default:
|
---|
| 732 | AssertMsg(!dataCb.uProcessed, ("Processed data is not 0 in undefined input state\n"));
|
---|
| 733 | break;
|
---|
| 734 | }
|
---|
[42634] | 735 |
|
---|
[47469] | 736 | if (inputStatus != ProcessInputStatus_Undefined)
|
---|
| 737 | {
|
---|
[47627] | 738 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 739 |
|
---|
| 740 | /* Copy over necessary data before releasing lock again. */
|
---|
| 741 | uint32_t uPID = mData.mPID;
|
---|
| 742 | /** @todo Also handle mSession? */
|
---|
| 743 |
|
---|
| 744 | alock.release(); /* Release lock before firing off event. */
|
---|
| 745 |
|
---|
[85300] | 746 | ::FireGuestProcessInputNotifyEvent(mEventSource, mSession, this, uPID, 0 /* StdIn */, dataCb.uProcessed, inputStatus);
|
---|
[47469] | 747 | }
|
---|
[42634] | 748 | }
|
---|
[42234] | 749 |
|
---|
[49349] | 750 | LogFlowFuncLeaveRC(vrc);
|
---|
[42721] | 751 | return vrc;
|
---|
[42214] | 752 | }
|
---|
| 753 |
|
---|
[92897] | 754 | /**
|
---|
| 755 | * Notifies of an I/O operation of the guest process.
|
---|
| 756 | *
|
---|
| 757 | * @returns VERR_NOT_IMPLEMENTED -- not implemented yet.
|
---|
| 758 | * @param pCbCtx Host callback context.
|
---|
| 759 | * @param pSvcCbData Host callback data.
|
---|
| 760 | */
|
---|
[50709] | 761 | int GuestProcess::i_onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
|
---|
[42461] | 762 | {
|
---|
[45415] | 763 | AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
|
---|
| 764 | AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
|
---|
[42461] | 765 |
|
---|
[44863] | 766 | return VERR_NOT_IMPLEMENTED;
|
---|
[42461] | 767 | }
|
---|
| 768 |
|
---|
[92897] | 769 | /**
|
---|
| 770 | * Sets (reports) the current running status of the guest process.
|
---|
| 771 | *
|
---|
| 772 | * @returns VBox status code.
|
---|
| 773 | * @param pCbCtx Host callback context.
|
---|
| 774 | * @param pSvcCbData Host callback data.
|
---|
| 775 | *
|
---|
| 776 | * @note Takes the write lock.
|
---|
| 777 | */
|
---|
[50709] | 778 | int GuestProcess::i_onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
|
---|
[42214] | 779 | {
|
---|
[47469] | 780 | AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
|
---|
[44863] | 781 | AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
|
---|
[42354] | 782 |
|
---|
[44863] | 783 | if (pSvcCbData->mParms < 5)
|
---|
| 784 | return VERR_INVALID_PARAMETER;
|
---|
[42214] | 785 |
|
---|
[44863] | 786 | CALLBACKDATA_PROC_STATUS dataCb;
|
---|
| 787 | /* pSvcCb->mpaParms[0] always contains the context ID. */
|
---|
[75737] | 788 | int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
|
---|
[44945] | 789 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 790 | vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uStatus);
|
---|
[44945] | 791 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 792 | vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
|
---|
[44945] | 793 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 794 | vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[4], &dataCb.pvData, &dataCb.cbData);
|
---|
[44945] | 795 | AssertRCReturn(vrc, vrc);
|
---|
[44863] | 796 |
|
---|
[45415] | 797 | LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32\n",
|
---|
| 798 | dataCb.uPID, dataCb.uStatus, dataCb.uFlags));
|
---|
[44863] | 799 |
|
---|
[50709] | 800 | vrc = i_checkPID(dataCb.uPID);
|
---|
[47469] | 801 | if (RT_SUCCESS(vrc))
|
---|
[42234] | 802 | {
|
---|
[47469] | 803 | ProcessStatus_T procStatus = ProcessStatus_Undefined;
|
---|
[98272] | 804 | int vrcProc = VINF_SUCCESS;
|
---|
[42234] | 805 |
|
---|
[47469] | 806 | switch (dataCb.uStatus)
|
---|
[42234] | 807 | {
|
---|
[47469] | 808 | case PROC_STS_STARTED:
|
---|
| 809 | {
|
---|
| 810 | procStatus = ProcessStatus_Started;
|
---|
[47627] | 811 |
|
---|
| 812 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[47469] | 813 | mData.mPID = dataCb.uPID; /* Set the process PID. */
|
---|
| 814 | break;
|
---|
| 815 | }
|
---|
[42234] | 816 |
|
---|
[47469] | 817 | case PROC_STS_TEN:
|
---|
| 818 | {
|
---|
| 819 | procStatus = ProcessStatus_TerminatedNormally;
|
---|
[47627] | 820 |
|
---|
| 821 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[47469] | 822 | mData.mExitCode = dataCb.uFlags; /* Contains the exit code. */
|
---|
| 823 | break;
|
---|
| 824 | }
|
---|
[42234] | 825 |
|
---|
[47469] | 826 | case PROC_STS_TES:
|
---|
| 827 | {
|
---|
| 828 | procStatus = ProcessStatus_TerminatedSignal;
|
---|
[47627] | 829 |
|
---|
| 830 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[47469] | 831 | mData.mExitCode = dataCb.uFlags; /* Contains the signal. */
|
---|
| 832 | break;
|
---|
| 833 | }
|
---|
[42234] | 834 |
|
---|
[47469] | 835 | case PROC_STS_TEA:
|
---|
| 836 | {
|
---|
| 837 | procStatus = ProcessStatus_TerminatedAbnormally;
|
---|
| 838 | break;
|
---|
| 839 | }
|
---|
[42234] | 840 |
|
---|
[47469] | 841 | case PROC_STS_TOK:
|
---|
| 842 | {
|
---|
| 843 | procStatus = ProcessStatus_TimedOutKilled;
|
---|
| 844 | break;
|
---|
| 845 | }
|
---|
[42234] | 846 |
|
---|
[47469] | 847 | case PROC_STS_TOA:
|
---|
| 848 | {
|
---|
| 849 | procStatus = ProcessStatus_TimedOutAbnormally;
|
---|
| 850 | break;
|
---|
| 851 | }
|
---|
[42234] | 852 |
|
---|
[47469] | 853 | case PROC_STS_DWN:
|
---|
| 854 | {
|
---|
| 855 | procStatus = ProcessStatus_Down;
|
---|
| 856 | break;
|
---|
| 857 | }
|
---|
[42234] | 858 |
|
---|
[47469] | 859 | case PROC_STS_ERROR:
|
---|
| 860 | {
|
---|
[98272] | 861 | vrcProc = dataCb.uFlags; /* mFlags contains the IPRT error sent from the guest. */
|
---|
[47469] | 862 | procStatus = ProcessStatus_Error;
|
---|
| 863 | break;
|
---|
| 864 | }
|
---|
| 865 |
|
---|
| 866 | case PROC_STS_UNDEFINED:
|
---|
| 867 | default:
|
---|
| 868 | {
|
---|
| 869 | /* Silently skip this request. */
|
---|
| 870 | procStatus = ProcessStatus_Undefined;
|
---|
| 871 | break;
|
---|
| 872 | }
|
---|
[42354] | 873 | }
|
---|
[42234] | 874 |
|
---|
[98272] | 875 | LogFlowThisFunc(("Got vrc=%Rrc, procSts=%RU32, vrcProc=%Rrc\n", vrc, procStatus, vrcProc));
|
---|
[42354] | 876 |
|
---|
[47469] | 877 | /* Set the process status. */
|
---|
[98272] | 878 | int vrc2 = i_setProcessStatus(procStatus, vrcProc);
|
---|
[47469] | 879 | if (RT_SUCCESS(vrc))
|
---|
[94915] | 880 | vrc = vrc2;
|
---|
[47469] | 881 | }
|
---|
[43162] | 882 |
|
---|
[49349] | 883 | LogFlowFuncLeaveRC(vrc);
|
---|
[42721] | 884 | return vrc;
|
---|
[42214] | 885 | }
|
---|
| 886 |
|
---|
[92897] | 887 | /**
|
---|
| 888 | * Sets (reports) the current output status of the guest process.
|
---|
| 889 | *
|
---|
| 890 | * @returns VBox status code.
|
---|
| 891 | * @param pCbCtx Host callback context.
|
---|
| 892 | * @param pSvcCbData Host callback data.
|
---|
| 893 | */
|
---|
[50709] | 894 | int GuestProcess::i_onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
|
---|
[42214] | 895 | {
|
---|
[63248] | 896 | RT_NOREF(pCbCtx);
|
---|
[44863] | 897 | AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
|
---|
[42354] | 898 |
|
---|
[44863] | 899 | if (pSvcCbData->mParms < 5)
|
---|
| 900 | return VERR_INVALID_PARAMETER;
|
---|
[42214] | 901 |
|
---|
[44863] | 902 | CALLBACKDATA_PROC_OUTPUT dataCb;
|
---|
| 903 | /* pSvcCb->mpaParms[0] always contains the context ID. */
|
---|
[75737] | 904 | int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uPID);
|
---|
[44945] | 905 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 906 | vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uHandle);
|
---|
[44945] | 907 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 908 | vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[3], &dataCb.uFlags);
|
---|
[44945] | 909 | AssertRCReturn(vrc, vrc);
|
---|
[75737] | 910 | vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[4], &dataCb.pvData, &dataCb.cbData);
|
---|
[44945] | 911 | AssertRCReturn(vrc, vrc);
|
---|
[44863] | 912 |
|
---|
[45415] | 913 | LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RI32, pvData=%p, cbData=%RU32\n",
|
---|
| 914 | dataCb.uPID, dataCb.uHandle, dataCb.uFlags, dataCb.pvData, dataCb.cbData));
|
---|
[44863] | 915 |
|
---|
[50709] | 916 | vrc = i_checkPID(dataCb.uPID);
|
---|
[47469] | 917 | if (RT_SUCCESS(vrc))
|
---|
| 918 | {
|
---|
| 919 | com::SafeArray<BYTE> data((size_t)dataCb.cbData);
|
---|
| 920 | if (dataCb.cbData)
|
---|
| 921 | data.initFrom((BYTE*)dataCb.pvData, dataCb.cbData);
|
---|
[42234] | 922 |
|
---|
[85300] | 923 | ::FireGuestProcessOutputEvent(mEventSource, mSession, this,
|
---|
| 924 | mData.mPID, dataCb.uHandle, dataCb.cbData, ComSafeArrayAsInParam(data));
|
---|
[47469] | 925 | }
|
---|
[42234] | 926 |
|
---|
[49349] | 927 | LogFlowFuncLeaveRC(vrc);
|
---|
[42721] | 928 | return vrc;
|
---|
[42214] | 929 | }
|
---|
| 930 |
|
---|
[49504] | 931 | /**
|
---|
[77587] | 932 | * @copydoc GuestObject::i_onUnregister
|
---|
[49504] | 933 | */
|
---|
[77587] | 934 | int GuestProcess::i_onUnregister(void)
|
---|
[49504] | 935 | {
|
---|
| 936 | LogFlowThisFuncEnter();
|
---|
| 937 |
|
---|
| 938 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 939 |
|
---|
| 940 | int vrc = VINF_SUCCESS;
|
---|
| 941 |
|
---|
[49570] | 942 | /*
|
---|
[49504] | 943 | * Note: The event source stuff holds references to this object,
|
---|
| 944 | * so make sure that this is cleaned up *before* calling uninit().
|
---|
| 945 | */
|
---|
| 946 | if (!mEventSource.isNull())
|
---|
| 947 | {
|
---|
| 948 | mEventSource->UnregisterListener(mLocalListener);
|
---|
| 949 |
|
---|
| 950 | mLocalListener.setNull();
|
---|
| 951 | unconst(mEventSource).setNull();
|
---|
| 952 | }
|
---|
| 953 |
|
---|
| 954 | LogFlowFuncLeaveRC(vrc);
|
---|
| 955 | return vrc;
|
---|
| 956 | }
|
---|
| 957 |
|
---|
[77587] | 958 | /**
|
---|
| 959 | * @copydoc GuestObject::i_onSessionStatusChange
|
---|
| 960 | */
|
---|
| 961 | int GuestProcess::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
|
---|
| 962 | {
|
---|
| 963 | LogFlowThisFuncEnter();
|
---|
| 964 |
|
---|
| 965 | int vrc = VINF_SUCCESS;
|
---|
| 966 |
|
---|
| 967 | /* If the session now is in a terminated state, set the process status
|
---|
| 968 | * to "down", as there is not much else we can do now. */
|
---|
| 969 | if (GuestSession::i_isTerminated(enmSessionStatus))
|
---|
| 970 | {
|
---|
| 971 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 972 |
|
---|
[98272] | 973 | vrc = i_setProcessStatus(ProcessStatus_Down, VINF_SUCCESS /* vrcProc, ignored */);
|
---|
[77587] | 974 | }
|
---|
| 975 |
|
---|
| 976 | LogFlowFuncLeaveRC(vrc);
|
---|
| 977 | return vrc;
|
---|
| 978 | }
|
---|
| 979 |
|
---|
[92897] | 980 | /**
|
---|
| 981 | * Reads data from a guest file.
|
---|
| 982 | *
|
---|
| 983 | * @returns VBox status code.
|
---|
| 984 | * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
|
---|
| 985 | * @param uHandle Internal file handle to use for reading.
|
---|
| 986 | * @param uSize Size (in bytes) to read.
|
---|
| 987 | * @param uTimeoutMS Timeout (in ms) to wait.
|
---|
| 988 | * @param pvData Where to store the read data on success.
|
---|
| 989 | * @param cbData Size (in bytes) of \a pvData on input.
|
---|
| 990 | * @param pcbRead Where to return to size (in bytes) read on success.
|
---|
| 991 | * Optional.
|
---|
[98272] | 992 | * @param pvrcGuest Where to return the guest error when
|
---|
| 993 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 994 | *
|
---|
| 995 | * @note Takes the write lock.
|
---|
| 996 | */
|
---|
[50709] | 997 | int GuestProcess::i_readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS,
|
---|
[98272] | 998 | void *pvData, size_t cbData, uint32_t *pcbRead, int *pvrcGuest)
|
---|
[42214] | 999 | {
|
---|
[98272] | 1000 | LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%RU32, pvrcGuest=%p\n",
|
---|
| 1001 | mData.mPID, uHandle, uSize, uTimeoutMS, pvData, cbData, pvrcGuest));
|
---|
[42214] | 1002 | AssertReturn(uSize, VERR_INVALID_PARAMETER);
|
---|
[42507] | 1003 | AssertPtrReturn(pvData, VERR_INVALID_POINTER);
|
---|
[42214] | 1004 | AssertReturn(cbData >= uSize, VERR_INVALID_PARAMETER);
|
---|
[42461] | 1005 | /* pcbRead is optional. */
|
---|
[42214] | 1006 |
|
---|
[42461] | 1007 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 1008 |
|
---|
[45697] | 1009 | if ( mData.mStatus != ProcessStatus_Started
|
---|
| 1010 | /* Skip reading if the process wasn't started with the appropriate
|
---|
| 1011 | * flags. */
|
---|
[92707] | 1012 | || ( ( uHandle == GUEST_PROC_OUT_H_STDOUT
|
---|
| 1013 | || uHandle == GUEST_PROC_OUT_H_STDOUT_DEPRECATED)
|
---|
[45697] | 1014 | && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdOut))
|
---|
[92707] | 1015 | || ( uHandle == GUEST_PROC_OUT_H_STDERR
|
---|
[45697] | 1016 | && !(mData.mProcess.mFlags & ProcessCreateFlag_WaitForStdErr))
|
---|
| 1017 | )
|
---|
[42787] | 1018 | {
|
---|
| 1019 | if (pcbRead)
|
---|
| 1020 | *pcbRead = 0;
|
---|
[98272] | 1021 | if (pvrcGuest)
|
---|
| 1022 | *pvrcGuest = VINF_SUCCESS;
|
---|
[42716] | 1023 | return VINF_SUCCESS; /* Nothing to read anymore. */
|
---|
[42787] | 1024 | }
|
---|
[42461] | 1025 |
|
---|
[45780] | 1026 | int vrc;
|
---|
| 1027 |
|
---|
| 1028 | GuestWaitEvent *pEvent = NULL;
|
---|
[49349] | 1029 | GuestEventTypes eventTypes;
|
---|
[45780] | 1030 | try
|
---|
| 1031 | {
|
---|
[47469] | 1032 | /*
|
---|
| 1033 | * On Guest Additions < 4.3 there is no guarantee that the process status
|
---|
| 1034 | * change arrives *after* the output event, e.g. if this was the last output
|
---|
| 1035 | * block being read and the process will report status "terminate".
|
---|
| 1036 | * So just skip checking for process status change and only wait for the
|
---|
| 1037 | * output event.
|
---|
| 1038 | */
|
---|
[50727] | 1039 | if (mSession->i_getProtocolVersion() >= 2)
|
---|
[47469] | 1040 | eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
|
---|
[45780] | 1041 | eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
|
---|
| 1042 |
|
---|
[47469] | 1043 | vrc = registerWaitEvent(eventTypes, &pEvent);
|
---|
[45780] | 1044 | }
|
---|
[73505] | 1045 | catch (std::bad_alloc &)
|
---|
[45780] | 1046 | {
|
---|
| 1047 | vrc = VERR_NO_MEMORY;
|
---|
| 1048 | }
|
---|
| 1049 |
|
---|
| 1050 | if (RT_FAILURE(vrc))
|
---|
| 1051 | return vrc;
|
---|
| 1052 |
|
---|
[42721] | 1053 | if (RT_SUCCESS(vrc))
|
---|
[43162] | 1054 | {
|
---|
[45415] | 1055 | VBOXHGCMSVCPARM paParms[8];
|
---|
[42461] | 1056 | int i = 0;
|
---|
[75737] | 1057 | HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
|
---|
| 1058 | HGCMSvcSetU32(&paParms[i++], mData.mPID);
|
---|
| 1059 | HGCMSvcSetU32(&paParms[i++], uHandle);
|
---|
| 1060 | HGCMSvcSetU32(&paParms[i++], 0 /* Flags, none set yet. */);
|
---|
[42461] | 1061 |
|
---|
[45697] | 1062 | alock.release(); /* Drop the write lock before sending. */
|
---|
| 1063 |
|
---|
[76958] | 1064 | vrc = sendMessage(HOST_MSG_EXEC_GET_OUTPUT, i, paParms);
|
---|
[42461] | 1065 | }
|
---|
| 1066 |
|
---|
[42721] | 1067 | if (RT_SUCCESS(vrc))
|
---|
[50709] | 1068 | vrc = i_waitForOutput(pEvent, uHandle, uTimeoutMS,
|
---|
| 1069 | pvData, cbData, pcbRead);
|
---|
[42461] | 1070 |
|
---|
[47469] | 1071 | unregisterWaitEvent(pEvent);
|
---|
[45780] | 1072 |
|
---|
[49349] | 1073 | LogFlowFuncLeaveRC(vrc);
|
---|
[42721] | 1074 | return vrc;
|
---|
[42214] | 1075 | }
|
---|
| 1076 |
|
---|
[92897] | 1077 | /**
|
---|
| 1078 | * Sets (reports) the current (overall) status of the guest process.
|
---|
| 1079 | *
|
---|
| 1080 | * @returns VBox status code.
|
---|
| 1081 | * @param procStatus Guest process status to set.
|
---|
[98272] | 1082 | * @param vrcProc Guest process result code to set.
|
---|
[92897] | 1083 | *
|
---|
| 1084 | * @note Takes the write lock.
|
---|
| 1085 | */
|
---|
[98272] | 1086 | int GuestProcess::i_setProcessStatus(ProcessStatus_T procStatus, int vrcProc)
|
---|
[42160] | 1087 | {
|
---|
[47627] | 1088 | LogFlowThisFuncEnter();
|
---|
| 1089 |
|
---|
| 1090 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 1091 |
|
---|
[98272] | 1092 | LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, vrcProc=%Rrc\n", mData.mStatus, procStatus, vrcProc));
|
---|
[42272] | 1093 |
|
---|
[43162] | 1094 | if (procStatus == ProcessStatus_Error)
|
---|
| 1095 | {
|
---|
[98272] | 1096 | AssertMsg(RT_FAILURE(vrcProc), ("Guest vrcProc must be an error (%Rrc)\n", vrcProc));
|
---|
[43162] | 1097 | /* Do not allow overwriting an already set error. If this happens
|
---|
| 1098 | * this means we forgot some error checking/locking somewhere. */
|
---|
[98272] | 1099 | AssertMsg(RT_SUCCESS(mData.mLastError), ("Guest vrcProc already set (to %Rrc)\n", mData.mLastError));
|
---|
[43162] | 1100 | }
|
---|
| 1101 | else
|
---|
[98272] | 1102 | AssertMsg(RT_SUCCESS(vrcProc), ("Guest vrcProc must not be an error (%Rrc)\n", vrcProc));
|
---|
[42436] | 1103 |
|
---|
[94915] | 1104 | int vrc = VINF_SUCCESS;
|
---|
[47627] | 1105 |
|
---|
[47469] | 1106 | if (mData.mStatus != procStatus) /* Was there a process status change? */
|
---|
[45415] | 1107 | {
|
---|
[47817] | 1108 | mData.mStatus = procStatus;
|
---|
[98272] | 1109 | mData.mLastError = vrcProc;
|
---|
[42436] | 1110 |
|
---|
[45805] | 1111 | ComObjPtr<VirtualBoxErrorInfo> errorInfo;
|
---|
[94915] | 1112 | HRESULT hrc = errorInfo.createObject();
|
---|
| 1113 | ComAssertComRC(hrc);
|
---|
[47817] | 1114 | if (RT_FAILURE(mData.mLastError))
|
---|
[45415] | 1115 | {
|
---|
[94915] | 1116 | hrc = errorInfo->initEx(VBOX_E_IPRT_ERROR, mData.mLastError,
|
---|
| 1117 | COM_IIDOF(IGuestProcess), getComponentName(),
|
---|
| 1118 | i_guestErrorToString(mData.mLastError, mData.mProcess.mExecutable.c_str()));
|
---|
| 1119 | ComAssertComRC(hrc);
|
---|
[45415] | 1120 | }
|
---|
| 1121 |
|
---|
[47627] | 1122 | /* Copy over necessary data before releasing lock again. */
|
---|
| 1123 | uint32_t uPID = mData.mPID;
|
---|
| 1124 | /** @todo Also handle mSession? */
|
---|
| 1125 |
|
---|
| 1126 | alock.release(); /* Release lock before firing off event. */
|
---|
| 1127 |
|
---|
[85300] | 1128 | ::FireGuestProcessStateChangedEvent(mEventSource, mSession, this, uPID, procStatus, errorInfo);
|
---|
[47627] | 1129 | #if 0
|
---|
| 1130 | /*
|
---|
| 1131 | * On Guest Additions < 4.3 there is no guarantee that outstanding
|
---|
| 1132 | * requests will be delivered to the host after the process has ended,
|
---|
| 1133 | * so just cancel all waiting events here to not let clients run
|
---|
| 1134 | * into timeouts.
|
---|
| 1135 | */
|
---|
| 1136 | if ( mSession->getProtocolVersion() < 2
|
---|
| 1137 | && hasEnded())
|
---|
| 1138 | {
|
---|
| 1139 | LogFlowThisFunc(("Process ended, canceling outstanding wait events ...\n"));
|
---|
[94915] | 1140 | vrc = cancelWaitEvents();
|
---|
[47627] | 1141 | }
|
---|
| 1142 | #endif
|
---|
[45415] | 1143 | }
|
---|
| 1144 |
|
---|
[94915] | 1145 | return vrc;
|
---|
[42272] | 1146 | }
|
---|
| 1147 |
|
---|
[92897] | 1148 | /**
|
---|
| 1149 | * Starts the process on the guest.
|
---|
| 1150 | *
|
---|
| 1151 | * @returns VBox status code.
|
---|
| 1152 | * @param cMsTimeout Timeout (in ms) to wait for starting the process.
|
---|
[98272] | 1153 | * @param pvrcGuest Where to return the guest error when
|
---|
| 1154 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 1155 | *
|
---|
| 1156 | * @note Takes the write lock.
|
---|
| 1157 | */
|
---|
[98272] | 1158 | int GuestProcess::i_startProcess(uint32_t cMsTimeout, int *pvrcGuest)
|
---|
[42272] | 1159 | {
|
---|
[99120] | 1160 | LogFlowThisFunc(("cMsTimeout=%RU32, procExe=%s, cwd=%s, procTimeoutMS=%RU32, procFlags=%x, sessionID=%RU32\n",
|
---|
| 1161 | cMsTimeout, mData.mProcess.mExecutable.c_str(), mData.mProcess.mCwd.c_str(),
|
---|
| 1162 | mData.mProcess.mTimeoutMS, mData.mProcess.mFlags, mSession->i_getId()));
|
---|
[42160] | 1163 |
|
---|
[42411] | 1164 | /* Wait until the caller function (if kicked off by a thread)
|
---|
| 1165 | * has returned and continue operation. */
|
---|
| 1166 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[42160] | 1167 |
|
---|
[45415] | 1168 | mData.mStatus = ProcessStatus_Starting;
|
---|
[42160] | 1169 |
|
---|
[45780] | 1170 | int vrc;
|
---|
| 1171 |
|
---|
| 1172 | GuestWaitEvent *pEvent = NULL;
|
---|
[49349] | 1173 | GuestEventTypes eventTypes;
|
---|
[45780] | 1174 | try
|
---|
[43162] | 1175 | {
|
---|
[45780] | 1176 | eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
|
---|
[47469] | 1177 | vrc = registerWaitEvent(eventTypes, &pEvent);
|
---|
[45780] | 1178 | }
|
---|
[73505] | 1179 | catch (std::bad_alloc &)
|
---|
[45780] | 1180 | {
|
---|
| 1181 | vrc = VERR_NO_MEMORY;
|
---|
| 1182 | }
|
---|
| 1183 | if (RT_FAILURE(vrc))
|
---|
| 1184 | return vrc;
|
---|
[43062] | 1185 |
|
---|
[98272] | 1186 | vrc = i_startProcessInner(cMsTimeout, alock, pEvent, pvrcGuest);
|
---|
[63153] | 1187 |
|
---|
| 1188 | unregisterWaitEvent(pEvent);
|
---|
| 1189 |
|
---|
| 1190 | LogFlowFuncLeaveRC(vrc);
|
---|
| 1191 | return vrc;
|
---|
| 1192 | }
|
---|
| 1193 |
|
---|
[92897] | 1194 | /**
|
---|
| 1195 | * Helper function to start a process on the guest. Do not call directly!
|
---|
| 1196 | *
|
---|
| 1197 | * @returns VBox status code.
|
---|
| 1198 | * @param cMsTimeout Timeout (in ms) to wait for starting the process.
|
---|
| 1199 | * @param rLock Write lock to use for serialization.
|
---|
| 1200 | * @param pEvent Event to use for notifying waiters.
|
---|
[98272] | 1201 | * @param pvrcGuest Where to return the guest error when
|
---|
| 1202 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 1203 | */
|
---|
[98272] | 1204 | int GuestProcess::i_startProcessInner(uint32_t cMsTimeout, AutoWriteLock &rLock, GuestWaitEvent *pEvent, int *pvrcGuest)
|
---|
[63153] | 1205 | {
|
---|
[45780] | 1206 | GuestSession *pSession = mSession;
|
---|
| 1207 | AssertPtr(pSession);
|
---|
[55535] | 1208 | uint32_t const uProtocol = pSession->i_getProtocolVersion();
|
---|
[43062] | 1209 |
|
---|
[50727] | 1210 | const GuestCredentials &sessionCreds = pSession->i_getCredentials();
|
---|
[42160] | 1211 |
|
---|
[45780] | 1212 | /* Prepare arguments. */
|
---|
| 1213 | size_t cArgs = mData.mProcess.mArguments.size();
|
---|
[63153] | 1214 | if (cArgs >= 128*1024)
|
---|
| 1215 | return VERR_BUFFER_OVERFLOW;
|
---|
[43062] | 1216 |
|
---|
[99120] | 1217 | Guest *pGuest = mSession->i_getParent();
|
---|
| 1218 | AssertPtr(pGuest);
|
---|
| 1219 | const uint64_t fGuestControlFeatures0 = pGuest->i_getGuestControlFeatures0();
|
---|
| 1220 |
|
---|
| 1221 | /* Check if the Guest Additions support setting the current working directory for the new process
|
---|
| 1222 | * if the caller wants to set one, and bail out early if it doesn't. */
|
---|
| 1223 | if ( !mData.mProcess.mCwd.isEmpty()
|
---|
| 1224 | && ( uProtocol < 2
|
---|
| 1225 | || !(fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_CWD)))
|
---|
| 1226 | {
|
---|
| 1227 | LogRel2(("Guest Control: Installed Guest Addtions don't support setting the current working directory to '%s'!\n",
|
---|
| 1228 | mData.mProcess.mCwd.c_str()));
|
---|
| 1229 | return VERR_NOT_SUPPORTED;
|
---|
| 1230 | }
|
---|
| 1231 |
|
---|
[84243] | 1232 | size_t cbArgs = 0;
|
---|
[63153] | 1233 | char *pszArgs = NULL;
|
---|
| 1234 | int vrc = VINF_SUCCESS;
|
---|
[99120] | 1235 |
|
---|
[63153] | 1236 | if (cArgs)
|
---|
[45780] | 1237 | {
|
---|
[55535] | 1238 | char const **papszArgv = (char const **)RTMemAlloc((cArgs + 1) * sizeof(papszArgv[0]));
|
---|
[45780] | 1239 | AssertReturn(papszArgv, VERR_NO_MEMORY);
|
---|
| 1240 |
|
---|
[55535] | 1241 | for (size_t i = 0; i < cArgs; i++)
|
---|
[45780] | 1242 | {
|
---|
[55535] | 1243 | papszArgv[i] = mData.mProcess.mArguments[i].c_str();
|
---|
| 1244 | AssertPtr(papszArgv[i]);
|
---|
[42160] | 1245 | }
|
---|
[45780] | 1246 | papszArgv[cArgs] = NULL;
|
---|
[42160] | 1247 |
|
---|
[83405] | 1248 | /* If the Guest Additions don't support using argv[0] correctly (< 6.1.x), don't supply it. */
|
---|
[83419] | 1249 | if (!(fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_ARGV0))
|
---|
[55535] | 1250 | vrc = RTGetOptArgvToString(&pszArgs, papszArgv + 1, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
|
---|
[83405] | 1251 | else /* ... else send the whole argv, including argv[0]. */
|
---|
[55491] | 1252 | vrc = RTGetOptArgvToString(&pszArgs, papszArgv, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
|
---|
[43062] | 1253 |
|
---|
[55535] | 1254 | RTMemFree(papszArgv);
|
---|
[63153] | 1255 | if (RT_FAILURE(vrc))
|
---|
| 1256 | return vrc;
|
---|
| 1257 |
|
---|
[84243] | 1258 | /* Note! No direct returns after this. */
|
---|
[84261] | 1259 | }
|
---|
[84243] | 1260 |
|
---|
[84261] | 1261 | /* Calculate arguments size (in bytes). */
|
---|
| 1262 | AssertPtr(pszArgs);
|
---|
| 1263 | cbArgs = strlen(pszArgs) + 1; /* Include terminating zero. */
|
---|
[84243] | 1264 |
|
---|
[55599] | 1265 | /* Prepare environment. The guest service dislikes the empty string at the end, so drop it. */
|
---|
[84260] | 1266 | size_t cbEnvBlock = 0; /* Shut up MSVC. */
|
---|
| 1267 | char *pszzEnvBlock = NULL; /* Ditto. */
|
---|
[84261] | 1268 | vrc = mData.mProcess.mEnvironmentChanges.queryUtf8Block(&pszzEnvBlock, &cbEnvBlock);
|
---|
[45780] | 1269 | if (RT_SUCCESS(vrc))
|
---|
| 1270 | {
|
---|
[55599] | 1271 | Assert(cbEnvBlock > 0);
|
---|
| 1272 | cbEnvBlock--;
|
---|
[84260] | 1273 | AssertPtr(pszzEnvBlock);
|
---|
[55599] | 1274 |
|
---|
[45780] | 1275 | /* Prepare HGCM call. */
|
---|
| 1276 | VBOXHGCMSVCPARM paParms[16];
|
---|
| 1277 | int i = 0;
|
---|
[75737] | 1278 | HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
|
---|
| 1279 | HGCMSvcSetRTCStr(&paParms[i++], mData.mProcess.mExecutable);
|
---|
| 1280 | HGCMSvcSetU32(&paParms[i++], mData.mProcess.mFlags);
|
---|
| 1281 | HGCMSvcSetU32(&paParms[i++], (uint32_t)mData.mProcess.mArguments.size());
|
---|
| 1282 | HGCMSvcSetPv(&paParms[i++], pszArgs, (uint32_t)cbArgs);
|
---|
| 1283 | HGCMSvcSetU32(&paParms[i++], mData.mProcess.mEnvironmentChanges.count());
|
---|
| 1284 | HGCMSvcSetU32(&paParms[i++], (uint32_t)cbEnvBlock);
|
---|
| 1285 | HGCMSvcSetPv(&paParms[i++], pszzEnvBlock, (uint32_t)cbEnvBlock);
|
---|
[45780] | 1286 | if (uProtocol < 2)
|
---|
| 1287 | {
|
---|
| 1288 | /* In protocol v1 (VBox < 4.3) the credentials were part of the execution
|
---|
| 1289 | * call. In newer protocols these credentials are part of the opened guest
|
---|
| 1290 | * session, so not needed anymore here. */
|
---|
[75737] | 1291 | HGCMSvcSetRTCStr(&paParms[i++], sessionCreds.mUser);
|
---|
| 1292 | HGCMSvcSetRTCStr(&paParms[i++], sessionCreds.mPassword);
|
---|
[42160] | 1293 | }
|
---|
[45780] | 1294 | /*
|
---|
| 1295 | * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
|
---|
| 1296 | * until the process was started - the process itself then gets an infinite timeout for execution.
|
---|
| 1297 | * This is handy when we want to start a process inside a worker thread within a certain timeout
|
---|
| 1298 | * but let the started process perform lengthly operations then.
|
---|
| 1299 | */
|
---|
| 1300 | if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
|
---|
[75737] | 1301 | HGCMSvcSetU32(&paParms[i++], UINT32_MAX /* Infinite timeout */);
|
---|
[45780] | 1302 | else
|
---|
[75737] | 1303 | HGCMSvcSetU32(&paParms[i++], mData.mProcess.mTimeoutMS);
|
---|
[45780] | 1304 | if (uProtocol >= 2)
|
---|
| 1305 | {
|
---|
[75737] | 1306 | HGCMSvcSetU32(&paParms[i++], mData.mProcess.mPriority);
|
---|
[45780] | 1307 | /* CPU affinity: We only support one CPU affinity block at the moment,
|
---|
| 1308 | * so that makes up to 64 CPUs total. This can be more in the future. */
|
---|
[75737] | 1309 | HGCMSvcSetU32(&paParms[i++], 1);
|
---|
[45780] | 1310 | /* The actual CPU affinity blocks. */
|
---|
[75737] | 1311 | HGCMSvcSetPv(&paParms[i++], (void *)&mData.mProcess.mAffinity, sizeof(mData.mProcess.mAffinity));
|
---|
[99120] | 1312 | /* Supply working directory, if guest supports it. */
|
---|
| 1313 | if (fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_CWD)
|
---|
| 1314 | HGCMSvcSetRTCStr(&paParms[i++], mData.mProcess.mCwd);
|
---|
[45780] | 1315 | }
|
---|
[42160] | 1316 |
|
---|
[63153] | 1317 | rLock.release(); /* Drop the write lock before sending. */
|
---|
[42214] | 1318 |
|
---|
[76958] | 1319 | vrc = sendMessage(HOST_MSG_EXEC_CMD, i, paParms);
|
---|
[45780] | 1320 | if (RT_FAILURE(vrc))
|
---|
[42214] | 1321 | {
|
---|
[94915] | 1322 | int vrc2 = i_setProcessStatus(ProcessStatus_Error, vrc);
|
---|
| 1323 | AssertRC(vrc2);
|
---|
[42214] | 1324 | }
|
---|
[55588] | 1325 |
|
---|
[55591] | 1326 | mData.mProcess.mEnvironmentChanges.freeUtf8Block(pszzEnvBlock);
|
---|
[42160] | 1327 | }
|
---|
| 1328 |
|
---|
[63153] | 1329 | RTStrFree(pszArgs);
|
---|
[45780] | 1330 |
|
---|
| 1331 | if (RT_SUCCESS(vrc))
|
---|
[98272] | 1332 | vrc = i_waitForStatusChange(pEvent, cMsTimeout, NULL /* Process status */, pvrcGuest);
|
---|
[42721] | 1333 | return vrc;
|
---|
[42354] | 1334 | }
|
---|
[42272] | 1335 |
|
---|
[92897] | 1336 | /**
|
---|
| 1337 | * Starts the process asynchronously (via worker thread) on the guest.
|
---|
| 1338 | *
|
---|
| 1339 | * @returns VBox status code.
|
---|
| 1340 | */
|
---|
[50709] | 1341 | int GuestProcess::i_startProcessAsync(void)
|
---|
[42354] | 1342 | {
|
---|
| 1343 | LogFlowThisFuncEnter();
|
---|
| 1344 |
|
---|
[78753] | 1345 | /* Create the task: */
|
---|
| 1346 | GuestProcessStartTask *pTask = NULL;
|
---|
[42897] | 1347 | try
|
---|
[42354] | 1348 | {
|
---|
[58521] | 1349 | pTask = new GuestProcessStartTask(this);
|
---|
[42354] | 1350 | }
|
---|
[78753] | 1351 | catch (std::bad_alloc &)
|
---|
[42897] | 1352 | {
|
---|
[78753] | 1353 | LogFlowThisFunc(("out of memory\n"));
|
---|
| 1354 | return VERR_NO_MEMORY;
|
---|
[42897] | 1355 | }
|
---|
[78753] | 1356 | AssertReturnStmt(pTask->i_isOk(), delete pTask, E_FAIL); /* cannot fail for GuestProcessStartTask. */
|
---|
| 1357 | LogFlowThisFunc(("Successfully created GuestProcessStartTask object\n"));
|
---|
[42354] | 1358 |
|
---|
[78753] | 1359 | /* Start the thread (always consumes the task): */
|
---|
| 1360 | HRESULT hrc = pTask->createThread();
|
---|
| 1361 | pTask = NULL;
|
---|
| 1362 | if (SUCCEEDED(hrc))
|
---|
| 1363 | return VINF_SUCCESS;
|
---|
| 1364 | LogFlowThisFunc(("Failed to create thread for GuestProcessStartTask\n"));
|
---|
| 1365 | return VERR_GENERAL_FAILURE;
|
---|
[42160] | 1366 | }
|
---|
| 1367 |
|
---|
[92897] | 1368 | /**
|
---|
| 1369 | * Thread task which does the asynchronous starting of a guest process.
|
---|
| 1370 | *
|
---|
| 1371 | * @returns VBox status code.
|
---|
| 1372 | * @param pTask Process start task (context) to process.
|
---|
| 1373 | */
|
---|
[42897] | 1374 | /* static */
|
---|
[71272] | 1375 | int GuestProcess::i_startProcessThreadTask(GuestProcessStartTask *pTask)
|
---|
[42160] | 1376 | {
|
---|
[63186] | 1377 | LogFlowFunc(("pTask=%p\n", pTask));
|
---|
[42160] | 1378 |
|
---|
[50709] | 1379 | const ComObjPtr<GuestProcess> pProcess(pTask->i_process());
|
---|
[42160] | 1380 | Assert(!pProcess.isNull());
|
---|
| 1381 |
|
---|
[42354] | 1382 | AutoCaller autoCaller(pProcess);
|
---|
[98262] | 1383 | if (FAILED(autoCaller.hrc()))
|
---|
[71272] | 1384 | return VERR_COM_UNEXPECTED;
|
---|
[42160] | 1385 |
|
---|
[104003] | 1386 | int vrc = pProcess->i_startProcess(GSTCTL_DEFAULT_TIMEOUT_MS, NULL /* pvrcGuest, ignored */);
|
---|
[43162] | 1387 | /* Nothing to do here anymore. */
|
---|
[42411] | 1388 |
|
---|
[71272] | 1389 | LogFlowFunc(("pProcess=%p, vrc=%Rrc\n", (GuestProcess *)pProcess, vrc));
|
---|
| 1390 | return vrc;
|
---|
[42160] | 1391 | }
|
---|
| 1392 |
|
---|
[92897] | 1393 | /**
|
---|
| 1394 | * Terminates a guest process.
|
---|
| 1395 | *
|
---|
| 1396 | * @returns VBox status code.
|
---|
[93036] | 1397 | * @retval VWRN_INVALID_STATE if process not in running state (anymore).
|
---|
| 1398 | * @retval VERR_NOT_SUPPORTED if process termination is not supported on the guest.
|
---|
[92897] | 1399 | * @param uTimeoutMS Timeout (in ms) to wait for process termination.
|
---|
[98272] | 1400 | * @param pvrcGuest Where to return the guest error when
|
---|
| 1401 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 1402 | *
|
---|
| 1403 | * @note Takes the write lock.
|
---|
| 1404 | */
|
---|
[98272] | 1405 | int GuestProcess::i_terminateProcess(uint32_t uTimeoutMS, int *pvrcGuest)
|
---|
[42160] | 1406 | {
|
---|
[47469] | 1407 | LogFlowThisFunc(("uTimeoutMS=%RU32\n", uTimeoutMS));
|
---|
[42160] | 1408 |
|
---|
[42716] | 1409 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 1410 |
|
---|
[49504] | 1411 | int vrc = VINF_SUCCESS;
|
---|
| 1412 |
|
---|
[44863] | 1413 | if (mData.mStatus != ProcessStatus_Started)
|
---|
[47469] | 1414 | {
|
---|
[49504] | 1415 | LogFlowThisFunc(("Process not in started state (state is %RU32), skipping termination\n",
|
---|
[49440] | 1416 | mData.mStatus));
|
---|
[93036] | 1417 | vrc = VWRN_INVALID_STATE;
|
---|
[47469] | 1418 | }
|
---|
[49504] | 1419 | else
|
---|
[45780] | 1420 | {
|
---|
[49504] | 1421 | AssertPtr(mSession);
|
---|
| 1422 | /* Note: VBox < 4.3 (aka protocol version 1) does not
|
---|
| 1423 | * support this, so just skip. */
|
---|
[50727] | 1424 | if (mSession->i_getProtocolVersion() < 2)
|
---|
[49504] | 1425 | vrc = VERR_NOT_SUPPORTED;
|
---|
[44863] | 1426 |
|
---|
[49504] | 1427 | if (RT_SUCCESS(vrc))
|
---|
[47469] | 1428 | {
|
---|
[49504] | 1429 | GuestWaitEvent *pEvent = NULL;
|
---|
| 1430 | GuestEventTypes eventTypes;
|
---|
| 1431 | try
|
---|
| 1432 | {
|
---|
| 1433 | eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
|
---|
[45780] | 1434 |
|
---|
[49504] | 1435 | vrc = registerWaitEvent(eventTypes, &pEvent);
|
---|
| 1436 | }
|
---|
[73505] | 1437 | catch (std::bad_alloc &)
|
---|
[49504] | 1438 | {
|
---|
| 1439 | vrc = VERR_NO_MEMORY;
|
---|
| 1440 | }
|
---|
[45780] | 1441 |
|
---|
[49504] | 1442 | if (RT_FAILURE(vrc))
|
---|
| 1443 | return vrc;
|
---|
[45780] | 1444 |
|
---|
[49504] | 1445 | VBOXHGCMSVCPARM paParms[4];
|
---|
| 1446 | int i = 0;
|
---|
[75737] | 1447 | HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
|
---|
| 1448 | HGCMSvcSetU32(&paParms[i++], mData.mPID);
|
---|
[45780] | 1449 |
|
---|
[49504] | 1450 | alock.release(); /* Drop the write lock before sending. */
|
---|
| 1451 |
|
---|
[76958] | 1452 | vrc = sendMessage(HOST_MSG_EXEC_TERMINATE, i, paParms);
|
---|
[49504] | 1453 | if (RT_SUCCESS(vrc))
|
---|
[50709] | 1454 | vrc = i_waitForStatusChange(pEvent, uTimeoutMS,
|
---|
[98272] | 1455 | NULL /* ProcessStatus */, pvrcGuest);
|
---|
[49504] | 1456 | unregisterWaitEvent(pEvent);
|
---|
| 1457 | }
|
---|
[47469] | 1458 | }
|
---|
| 1459 |
|
---|
[49349] | 1460 | LogFlowFuncLeaveRC(vrc);
|
---|
[44863] | 1461 | return vrc;
|
---|
[42160] | 1462 | }
|
---|
| 1463 |
|
---|
[92897] | 1464 | /**
|
---|
| 1465 | * Converts given process status / flags and wait flag combination
|
---|
| 1466 | * to an overall process wait result.
|
---|
| 1467 | *
|
---|
| 1468 | * @returns Overall process wait result.
|
---|
| 1469 | * @param fWaitFlags Process wait flags to use for conversion.
|
---|
| 1470 | * @param oldStatus Old process status to use for conversion.
|
---|
| 1471 | * @param newStatus New process status to use for conversion.
|
---|
| 1472 | * @param uProcFlags Process flags to use for conversion.
|
---|
| 1473 | * @param uProtocol Guest Control protocol version to use for conversion.
|
---|
| 1474 | */
|
---|
[47469] | 1475 | /* static */
|
---|
[50709] | 1476 | ProcessWaitResult_T GuestProcess::i_waitFlagsToResultEx(uint32_t fWaitFlags,
|
---|
| 1477 | ProcessStatus_T oldStatus, ProcessStatus_T newStatus,
|
---|
| 1478 | uint32_t uProcFlags, uint32_t uProtocol)
|
---|
[42160] | 1479 | {
|
---|
[47469] | 1480 | ProcessWaitResult_T waitResult = ProcessWaitResult_None;
|
---|
[42234] | 1481 |
|
---|
[49349] | 1482 | switch (newStatus)
|
---|
[42411] | 1483 | {
|
---|
[49349] | 1484 | case ProcessStatus_TerminatedNormally:
|
---|
| 1485 | case ProcessStatus_TerminatedSignal:
|
---|
| 1486 | case ProcessStatus_TerminatedAbnormally:
|
---|
| 1487 | case ProcessStatus_Down:
|
---|
[49440] | 1488 | /* Nothing to wait for anymore. */
|
---|
[49349] | 1489 | waitResult = ProcessWaitResult_Terminate;
|
---|
| 1490 | break;
|
---|
[42411] | 1491 |
|
---|
[49349] | 1492 | case ProcessStatus_TimedOutKilled:
|
---|
| 1493 | case ProcessStatus_TimedOutAbnormally:
|
---|
[49440] | 1494 | /* Dito. */
|
---|
[49349] | 1495 | waitResult = ProcessWaitResult_Timeout;
|
---|
| 1496 | break;
|
---|
[42411] | 1497 |
|
---|
[49349] | 1498 | case ProcessStatus_Started:
|
---|
| 1499 | switch (oldStatus)
|
---|
[42787] | 1500 | {
|
---|
[49440] | 1501 | case ProcessStatus_Undefined:
|
---|
[49349] | 1502 | case ProcessStatus_Starting:
|
---|
[49440] | 1503 | /* Also wait for process start. */
|
---|
[49349] | 1504 | if (fWaitFlags & ProcessWaitForFlag_Start)
|
---|
| 1505 | waitResult = ProcessWaitResult_Start;
|
---|
| 1506 | else
|
---|
| 1507 | {
|
---|
| 1508 | /*
|
---|
[55540] | 1509 | * If ProcessCreateFlag_WaitForProcessStartOnly was specified on process creation the
|
---|
| 1510 | * caller is not interested in getting further process statuses -- so just don't notify
|
---|
| 1511 | * anything here anymore and return.
|
---|
| 1512 | */
|
---|
[49349] | 1513 | if (uProcFlags & ProcessCreateFlag_WaitForProcessStartOnly)
|
---|
| 1514 | waitResult = ProcessWaitResult_Start;
|
---|
| 1515 | }
|
---|
| 1516 | break;
|
---|
| 1517 |
|
---|
[49440] | 1518 | case ProcessStatus_Started:
|
---|
| 1519 | /* Only wait for process start. */
|
---|
[79239] | 1520 | if (fWaitFlags & ProcessWaitForFlag_Start)
|
---|
[49440] | 1521 | waitResult = ProcessWaitResult_Start;
|
---|
| 1522 | break;
|
---|
| 1523 |
|
---|
[49349] | 1524 | default:
|
---|
[49504] | 1525 | AssertMsgFailed(("Unhandled old status %RU32 before new status 'started'\n",
|
---|
[49440] | 1526 | oldStatus));
|
---|
[79239] | 1527 | if (fWaitFlags & ProcessWaitForFlag_Start)
|
---|
| 1528 | waitResult = ProcessWaitResult_Start;
|
---|
[49349] | 1529 | break;
|
---|
[42787] | 1530 | }
|
---|
[49349] | 1531 | break;
|
---|
[42411] | 1532 |
|
---|
[49440] | 1533 | case ProcessStatus_Error:
|
---|
| 1534 | /* Nothing to wait for anymore. */
|
---|
| 1535 | waitResult = ProcessWaitResult_Error;
|
---|
| 1536 | break;
|
---|
| 1537 |
|
---|
[49349] | 1538 | case ProcessStatus_Undefined:
|
---|
| 1539 | case ProcessStatus_Starting:
|
---|
[63153] | 1540 | case ProcessStatus_Terminating:
|
---|
| 1541 | case ProcessStatus_Paused:
|
---|
[49440] | 1542 | /* No result available yet, leave wait
|
---|
| 1543 | * flags untouched. */
|
---|
[49349] | 1544 | break;
|
---|
[72973] | 1545 | #ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
|
---|
| 1546 | case ProcessStatus_32BitHack: AssertFailedBreak(); /* (compiler warnings) */
|
---|
| 1547 | #endif
|
---|
[42411] | 1548 | }
|
---|
| 1549 |
|
---|
[49349] | 1550 | if (newStatus == ProcessStatus_Started)
|
---|
[43162] | 1551 | {
|
---|
[79283] | 1552 | /*
|
---|
[55540] | 1553 | * Filter out waits which are *not* supported using
|
---|
[49349] | 1554 | * older guest control Guest Additions.
|
---|
| 1555 | *
|
---|
| 1556 | */
|
---|
[79283] | 1557 | /** @todo ProcessWaitForFlag_Std* flags are not implemented yet. */
|
---|
[49349] | 1558 | if (uProtocol < 99) /* See @todo above. */
|
---|
| 1559 | {
|
---|
| 1560 | if ( waitResult == ProcessWaitResult_None
|
---|
| 1561 | /* We don't support waiting for stdin, out + err,
|
---|
| 1562 | * just skip waiting then. */
|
---|
| 1563 | && ( (fWaitFlags & ProcessWaitForFlag_StdIn)
|
---|
| 1564 | || (fWaitFlags & ProcessWaitForFlag_StdOut)
|
---|
| 1565 | || (fWaitFlags & ProcessWaitForFlag_StdErr)
|
---|
| 1566 | )
|
---|
[43162] | 1567 | )
|
---|
[49349] | 1568 | {
|
---|
| 1569 | /* Use _WaitFlagNotSupported because we don't know what to tell the caller. */
|
---|
| 1570 | waitResult = ProcessWaitResult_WaitFlagNotSupported;
|
---|
| 1571 | }
|
---|
[43162] | 1572 | }
|
---|
| 1573 | }
|
---|
[42436] | 1574 |
|
---|
[49349] | 1575 | #ifdef DEBUG
|
---|
[49504] | 1576 | LogFlowFunc(("oldStatus=%RU32, newStatus=%RU32, fWaitFlags=0x%x, waitResult=%RU32\n",
|
---|
[49349] | 1577 | oldStatus, newStatus, fWaitFlags, waitResult));
|
---|
| 1578 | #endif
|
---|
[47469] | 1579 | return waitResult;
|
---|
| 1580 | }
|
---|
| 1581 |
|
---|
[92897] | 1582 | /**
|
---|
| 1583 | * Converts given wait flags to an overall process wait result.
|
---|
| 1584 | *
|
---|
| 1585 | * @returns Overall process wait result.
|
---|
| 1586 | * @param fWaitFlags Process wait flags to use for conversion.
|
---|
| 1587 | */
|
---|
[50709] | 1588 | ProcessWaitResult_T GuestProcess::i_waitFlagsToResult(uint32_t fWaitFlags)
|
---|
[47469] | 1589 | {
|
---|
| 1590 | AssertPtr(mSession);
|
---|
[50709] | 1591 | return GuestProcess::i_waitFlagsToResultEx(fWaitFlags,
|
---|
[79239] | 1592 | mData.mStatus /* oldStatus */, mData.mStatus /* newStatus */,
|
---|
[50727] | 1593 | mData.mProcess.mFlags, mSession->i_getProtocolVersion());
|
---|
[47469] | 1594 | }
|
---|
| 1595 |
|
---|
[92897] | 1596 | /**
|
---|
| 1597 | * Waits for certain events of the guest process.
|
---|
| 1598 | *
|
---|
| 1599 | * @returns VBox status code.
|
---|
| 1600 | * @param fWaitFlags Process wait flags to wait for.
|
---|
| 1601 | * @param uTimeoutMS Timeout (in ms) to wait.
|
---|
| 1602 | * @param waitResult Where to return the process wait result on success.
|
---|
[98272] | 1603 | * @param pvrcGuest Where to return the guest error when
|
---|
| 1604 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 1605 | * @note Takes the read lock.
|
---|
| 1606 | */
|
---|
[98272] | 1607 | int GuestProcess::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, ProcessWaitResult_T &waitResult, int *pvrcGuest)
|
---|
[47469] | 1608 | {
|
---|
| 1609 | AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
|
---|
| 1610 |
|
---|
[49349] | 1611 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[47469] | 1612 |
|
---|
[98272] | 1613 | LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, procStatus=%RU32, vrcProc=%Rrc, pvrcGuest=%p\n",
|
---|
| 1614 | fWaitFlags, uTimeoutMS, mData.mStatus, mData.mLastError, pvrcGuest));
|
---|
[47469] | 1615 |
|
---|
| 1616 | /* Did some error occur before? Then skip waiting and return. */
|
---|
[49349] | 1617 | ProcessStatus_T curStatus = mData.mStatus;
|
---|
| 1618 | if (curStatus == ProcessStatus_Error)
|
---|
[47469] | 1619 | {
|
---|
| 1620 | waitResult = ProcessWaitResult_Error;
|
---|
[50899] | 1621 | AssertMsg(RT_FAILURE(mData.mLastError),
|
---|
[98272] | 1622 | ("No error vrc (%Rrc) set when guest process indicated an error\n", mData.mLastError));
|
---|
| 1623 | if (pvrcGuest)
|
---|
| 1624 | *pvrcGuest = mData.mLastError; /* Return last set error. */
|
---|
| 1625 | LogFlowThisFunc(("Process is in error state (vrcGuest=%Rrc)\n", mData.mLastError));
|
---|
[47469] | 1626 | return VERR_GSTCTL_GUEST_ERROR;
|
---|
| 1627 | }
|
---|
| 1628 |
|
---|
[50709] | 1629 | waitResult = i_waitFlagsToResult(fWaitFlags);
|
---|
[42411] | 1630 |
|
---|
[43162] | 1631 | /* No waiting needed? Return immediately using the last set error. */
|
---|
| 1632 | if (waitResult != ProcessWaitResult_None)
|
---|
| 1633 | {
|
---|
[98272] | 1634 | if (pvrcGuest)
|
---|
| 1635 | *pvrcGuest = mData.mLastError; /* Return last set error (if any). */
|
---|
| 1636 | LogFlowThisFunc(("Nothing to wait for (vrcGuest=%Rrc)\n", mData.mLastError));
|
---|
[47817] | 1637 | return RT_SUCCESS(mData.mLastError) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
|
---|
[43162] | 1638 | }
|
---|
| 1639 |
|
---|
[49405] | 1640 | /* Adjust timeout. Passing 0 means RT_INDEFINITE_WAIT. */
|
---|
| 1641 | if (!uTimeoutMS)
|
---|
| 1642 | uTimeoutMS = RT_INDEFINITE_WAIT;
|
---|
| 1643 |
|
---|
[45780] | 1644 | int vrc;
|
---|
| 1645 |
|
---|
| 1646 | GuestWaitEvent *pEvent = NULL;
|
---|
[49349] | 1647 | GuestEventTypes eventTypes;
|
---|
[45780] | 1648 | try
|
---|
| 1649 | {
|
---|
| 1650 | eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
|
---|
| 1651 |
|
---|
[47469] | 1652 | vrc = registerWaitEvent(eventTypes, &pEvent);
|
---|
[45780] | 1653 | }
|
---|
[73505] | 1654 | catch (std::bad_alloc &)
|
---|
[45780] | 1655 | {
|
---|
| 1656 | vrc = VERR_NO_MEMORY;
|
---|
| 1657 | }
|
---|
| 1658 |
|
---|
| 1659 | if (RT_FAILURE(vrc))
|
---|
| 1660 | return vrc;
|
---|
| 1661 |
|
---|
[49440] | 1662 | alock.release(); /* Release lock before waiting. */
|
---|
| 1663 |
|
---|
[47469] | 1664 | /*
|
---|
| 1665 | * Do the actual waiting.
|
---|
| 1666 | */
|
---|
[49349] | 1667 | ProcessStatus_T newStatus = ProcessStatus_Undefined;
|
---|
[47469] | 1668 | uint64_t u64StartMS = RTTimeMilliTS();
|
---|
| 1669 | for (;;)
|
---|
[42897] | 1670 | {
|
---|
[47905] | 1671 | uint64_t u64ElapsedMS = RTTimeMilliTS() - u64StartMS;
|
---|
[47469] | 1672 | if ( uTimeoutMS != RT_INDEFINITE_WAIT
|
---|
[47905] | 1673 | && u64ElapsedMS >= uTimeoutMS)
|
---|
[45415] | 1674 | {
|
---|
[47469] | 1675 | vrc = VERR_TIMEOUT;
|
---|
| 1676 | break;
|
---|
| 1677 | }
|
---|
[45415] | 1678 |
|
---|
[50709] | 1679 | vrc = i_waitForStatusChange(pEvent,
|
---|
[98272] | 1680 | uTimeoutMS == RT_INDEFINITE_WAIT ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS,
|
---|
| 1681 | &newStatus, pvrcGuest);
|
---|
[47469] | 1682 | if (RT_SUCCESS(vrc))
|
---|
| 1683 | {
|
---|
[47470] | 1684 | alock.acquire();
|
---|
[45415] | 1685 |
|
---|
[50709] | 1686 | waitResult = i_waitFlagsToResultEx(fWaitFlags, curStatus, newStatus,
|
---|
[50727] | 1687 | mData.mProcess.mFlags, mSession->i_getProtocolVersion());
|
---|
[49349] | 1688 | #ifdef DEBUG
|
---|
[49504] | 1689 | LogFlowThisFunc(("Got new status change: fWaitFlags=0x%x, newStatus=%RU32, waitResult=%RU32\n",
|
---|
[49349] | 1690 | fWaitFlags, newStatus, waitResult));
|
---|
| 1691 | #endif
|
---|
[98608] | 1692 | if (waitResult != ProcessWaitResult_None) /* We got a waiting result. */
|
---|
[45415] | 1693 | break;
|
---|
| 1694 | }
|
---|
[47469] | 1695 | else /* Waiting failed, bail out. */
|
---|
| 1696 | break;
|
---|
[47627] | 1697 |
|
---|
| 1698 | alock.release(); /* Don't hold lock in next waiting round. */
|
---|
[42897] | 1699 | }
|
---|
[45415] | 1700 |
|
---|
[47469] | 1701 | unregisterWaitEvent(pEvent);
|
---|
[45780] | 1702 |
|
---|
[98608] | 1703 | LogFlowThisFunc(("returns %Rrc - waitResult=%RU32 newStatus=%RU32 *pvrcGuest=%Rrc\n",
|
---|
| 1704 | vrc, waitResult, newStatus, pvrcGuest ? *pvrcGuest : -VERR_IPE_UNINITIALIZED_STATUS));
|
---|
[45415] | 1705 | return vrc;
|
---|
| 1706 | }
|
---|
| 1707 |
|
---|
[92897] | 1708 | /**
|
---|
| 1709 | * Waits for a guest process input notification.
|
---|
| 1710 | *
|
---|
| 1711 | * @param pEvent Wait event to use for waiting.
|
---|
| 1712 | * @param uHandle Guest process file handle to wait for.
|
---|
| 1713 | * @param uTimeoutMS Timeout (in ms) to wait.
|
---|
| 1714 | * @param pInputStatus Where to return the process input status on success.
|
---|
| 1715 | * @param pcbProcessed Where to return the processed input (in bytes) on success.
|
---|
| 1716 | */
|
---|
[50709] | 1717 | int GuestProcess::i_waitForInputNotify(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
|
---|
| 1718 | ProcessInputStatus_T *pInputStatus, uint32_t *pcbProcessed)
|
---|
[45415] | 1719 | {
|
---|
[63248] | 1720 | RT_NOREF(uHandle);
|
---|
[45780] | 1721 | AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
|
---|
[45482] | 1722 |
|
---|
[45780] | 1723 | VBoxEventType_T evtType;
|
---|
| 1724 | ComPtr<IEvent> pIEvent;
|
---|
| 1725 | int vrc = waitForEvent(pEvent, uTimeoutMS,
|
---|
[49440] | 1726 | &evtType, pIEvent.asOutParam());
|
---|
[45780] | 1727 | if (RT_SUCCESS(vrc))
|
---|
[43162] | 1728 | {
|
---|
[45780] | 1729 | if (evtType == VBoxEventType_OnGuestProcessInputNotify)
|
---|
| 1730 | {
|
---|
| 1731 | ComPtr<IGuestProcessInputNotifyEvent> pProcessEvent = pIEvent;
|
---|
| 1732 | Assert(!pProcessEvent.isNull());
|
---|
[42411] | 1733 |
|
---|
[45780] | 1734 | if (pInputStatus)
|
---|
[45415] | 1735 | {
|
---|
[98272] | 1736 | HRESULT hrc2 = pProcessEvent->COMGETTER(Status)(pInputStatus);
|
---|
| 1737 | ComAssertComRC(hrc2);
|
---|
[45415] | 1738 | }
|
---|
[45780] | 1739 | if (pcbProcessed)
|
---|
[45415] | 1740 | {
|
---|
[98272] | 1741 | HRESULT hrc2 = pProcessEvent->COMGETTER(Processed)((ULONG*)pcbProcessed);
|
---|
| 1742 | ComAssertComRC(hrc2);
|
---|
[45415] | 1743 | }
|
---|
[43162] | 1744 | }
|
---|
[45780] | 1745 | else
|
---|
| 1746 | vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
|
---|
[45415] | 1747 | }
|
---|
[42673] | 1748 |
|
---|
[98272] | 1749 | LogFlowThisFunc(("Returning pEvent=%p, uHandle=%RU32, vrc=%Rrc\n", pEvent, uHandle, vrc));
|
---|
[45415] | 1750 | return vrc;
|
---|
| 1751 | }
|
---|
| 1752 |
|
---|
[92897] | 1753 | /**
|
---|
| 1754 | * Waits for a guest process input notification.
|
---|
| 1755 | *
|
---|
| 1756 | * @returns VBox status code.
|
---|
| 1757 | * @param pEvent Wait event to use for waiting.
|
---|
| 1758 | * @param uHandle Guest process file handle to wait for.
|
---|
| 1759 | * @param uTimeoutMS Timeout (in ms) to wait.
|
---|
| 1760 | * @param pvData Where to store the guest process output on success.
|
---|
| 1761 | * @param cbData Size (in bytes) of \a pvData.
|
---|
| 1762 | * @param pcbRead Where to return the size (in bytes) read.
|
---|
| 1763 | */
|
---|
[50709] | 1764 | int GuestProcess::i_waitForOutput(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS,
|
---|
| 1765 | void *pvData, size_t cbData, uint32_t *pcbRead)
|
---|
[45482] | 1766 | {
|
---|
[45780] | 1767 | AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
|
---|
[47627] | 1768 | /* pvData is optional. */
|
---|
| 1769 | /* cbData is optional. */
|
---|
| 1770 | /* pcbRead is optional. */
|
---|
[45482] | 1771 |
|
---|
[47627] | 1772 | LogFlowThisFunc(("cEventTypes=%zu, pEvent=%p, uHandle=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu, pcbRead=%p\n",
|
---|
| 1773 | pEvent->TypeCount(), pEvent, uHandle, uTimeoutMS, pvData, cbData, pcbRead));
|
---|
| 1774 |
|
---|
[45780] | 1775 | int vrc;
|
---|
[45482] | 1776 |
|
---|
| 1777 | VBoxEventType_T evtType;
|
---|
[45780] | 1778 | ComPtr<IEvent> pIEvent;
|
---|
[45482] | 1779 | do
|
---|
| 1780 | {
|
---|
[45780] | 1781 | vrc = waitForEvent(pEvent, uTimeoutMS,
|
---|
[49440] | 1782 | &evtType, pIEvent.asOutParam());
|
---|
[45780] | 1783 | if (RT_SUCCESS(vrc))
|
---|
[45415] | 1784 | {
|
---|
[45780] | 1785 | if (evtType == VBoxEventType_OnGuestProcessOutput)
|
---|
| 1786 | {
|
---|
| 1787 | ComPtr<IGuestProcessOutputEvent> pProcessEvent = pIEvent;
|
---|
| 1788 | Assert(!pProcessEvent.isNull());
|
---|
[45415] | 1789 |
|
---|
[45780] | 1790 | ULONG uHandleEvent;
|
---|
[98272] | 1791 | HRESULT hrc = pProcessEvent->COMGETTER(Handle)(&uHandleEvent);
|
---|
| 1792 | if ( SUCCEEDED(hrc)
|
---|
[47627] | 1793 | && uHandleEvent == uHandle)
|
---|
[45415] | 1794 | {
|
---|
[45780] | 1795 | if (pvData)
|
---|
[45415] | 1796 | {
|
---|
[45780] | 1797 | com::SafeArray <BYTE> data;
|
---|
[98272] | 1798 | hrc = pProcessEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
|
---|
| 1799 | ComAssertComRC(hrc);
|
---|
[45780] | 1800 | size_t cbRead = data.size();
|
---|
| 1801 | if (cbRead)
|
---|
[45697] | 1802 | {
|
---|
[45780] | 1803 | if (cbRead <= cbData)
|
---|
| 1804 | {
|
---|
| 1805 | /* Copy data from event into our buffer. */
|
---|
| 1806 | memcpy(pvData, data.raw(), data.size());
|
---|
| 1807 | }
|
---|
| 1808 | else
|
---|
| 1809 | vrc = VERR_BUFFER_OVERFLOW;
|
---|
[49349] | 1810 |
|
---|
[98272] | 1811 | LogFlowThisFunc(("Read %zu bytes (uHandle=%RU32), vrc=%Rrc\n", cbRead, uHandleEvent, vrc));
|
---|
[45697] | 1812 | }
|
---|
[45415] | 1813 | }
|
---|
[47627] | 1814 |
|
---|
| 1815 | if ( RT_SUCCESS(vrc)
|
---|
| 1816 | && pcbRead)
|
---|
[45780] | 1817 | {
|
---|
| 1818 | ULONG cbRead;
|
---|
[98272] | 1819 | hrc = pProcessEvent->COMGETTER(Processed)(&cbRead);
|
---|
| 1820 | ComAssertComRC(hrc);
|
---|
[45780] | 1821 | *pcbRead = (uint32_t)cbRead;
|
---|
| 1822 | }
|
---|
| 1823 |
|
---|
| 1824 | break;
|
---|
[45482] | 1825 | }
|
---|
[98272] | 1826 | else if (FAILED(hrc))
|
---|
[47627] | 1827 | vrc = VERR_COM_UNEXPECTED;
|
---|
[45415] | 1828 | }
|
---|
[45780] | 1829 | else
|
---|
| 1830 | vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
|
---|
[45415] | 1831 | }
|
---|
| 1832 |
|
---|
[47627] | 1833 | } while (vrc == VINF_SUCCESS);
|
---|
[45415] | 1834 |
|
---|
[47627] | 1835 | if ( vrc != VINF_SUCCESS
|
---|
| 1836 | && pcbRead)
|
---|
| 1837 | {
|
---|
| 1838 | *pcbRead = 0;
|
---|
| 1839 | }
|
---|
| 1840 |
|
---|
[49349] | 1841 | LogFlowFuncLeaveRC(vrc);
|
---|
[42721] | 1842 | return vrc;
|
---|
[42673] | 1843 | }
|
---|
| 1844 |
|
---|
[78234] | 1845 | /**
|
---|
[92897] | 1846 | * Waits for a guest process status change.
|
---|
[78234] | 1847 | *
|
---|
[92897] | 1848 | * @returns VBox status code.
|
---|
| 1849 | * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
|
---|
| 1850 | * @param pEvent Guest wait event to wait for.
|
---|
| 1851 | * @param uTimeoutMS Timeout (in ms) to wait.
|
---|
| 1852 | * @param pProcessStatus Where to return the process status on success.
|
---|
[98272] | 1853 | * @param pvrcGuest Where to return the guest error when
|
---|
| 1854 | * VERR_GSTCTL_GUEST_ERROR was returned.
|
---|
[78234] | 1855 | */
|
---|
[50709] | 1856 | int GuestProcess::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
|
---|
[98272] | 1857 | ProcessStatus_T *pProcessStatus, int *pvrcGuest)
|
---|
[45415] | 1858 | {
|
---|
[45780] | 1859 | AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
|
---|
[47469] | 1860 | /* pProcessStatus is optional. */
|
---|
[98272] | 1861 | /* pvrcGuest is optional. */
|
---|
[45415] | 1862 |
|
---|
[45482] | 1863 | VBoxEventType_T evtType;
|
---|
[45780] | 1864 | ComPtr<IEvent> pIEvent;
|
---|
| 1865 | int vrc = waitForEvent(pEvent, uTimeoutMS,
|
---|
| 1866 | &evtType, pIEvent.asOutParam());
|
---|
| 1867 | if (RT_SUCCESS(vrc))
|
---|
[45415] | 1868 | {
|
---|
[45482] | 1869 | Assert(evtType == VBoxEventType_OnGuestProcessStateChanged);
|
---|
[45780] | 1870 | ComPtr<IGuestProcessStateChangedEvent> pProcessEvent = pIEvent;
|
---|
[45482] | 1871 | Assert(!pProcessEvent.isNull());
|
---|
[45415] | 1872 |
|
---|
[49349] | 1873 | ProcessStatus_T procStatus;
|
---|
[98272] | 1874 | HRESULT hrc = pProcessEvent->COMGETTER(Status)(&procStatus);
|
---|
| 1875 | ComAssertComRC(hrc);
|
---|
[45482] | 1876 | if (pProcessStatus)
|
---|
[49349] | 1877 | *pProcessStatus = procStatus;
|
---|
[45415] | 1878 |
|
---|
[45805] | 1879 | ComPtr<IVirtualBoxErrorInfo> errorInfo;
|
---|
[98272] | 1880 | hrc = pProcessEvent->COMGETTER(Error)(errorInfo.asOutParam());
|
---|
| 1881 | ComAssertComRC(hrc);
|
---|
[45415] | 1882 |
|
---|
[45482] | 1883 | LONG lGuestRc;
|
---|
[98272] | 1884 | hrc = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
|
---|
| 1885 | ComAssertComRC(hrc);
|
---|
[47469] | 1886 |
|
---|
[98272] | 1887 | LogFlowThisFunc(("Got procStatus=%RU32, vrcGuest=%RI32 (%Rrc)\n", procStatus, lGuestRc, lGuestRc));
|
---|
[47469] | 1888 |
|
---|
[45482] | 1889 | if (RT_FAILURE((int)lGuestRc))
|
---|
| 1890 | vrc = VERR_GSTCTL_GUEST_ERROR;
|
---|
[45415] | 1891 |
|
---|
[98272] | 1892 | if (pvrcGuest)
|
---|
| 1893 | *pvrcGuest = (int)lGuestRc;
|
---|
[45415] | 1894 | }
|
---|
[98272] | 1895 | /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make pvrcGuest is set. */
|
---|
[98666] | 1896 | else if (pEvent->HasGuestError() && pvrcGuest)
|
---|
[98272] | 1897 | *pvrcGuest = pEvent->GuestResult();
|
---|
| 1898 | Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !pvrcGuest || *pvrcGuest != (int)0xcccccccc);
|
---|
[45415] | 1899 |
|
---|
[98608] | 1900 | LogFlowFunc(("LEAVE: %Rrc *pvrcGuest=%Rrc *pProcessStatus=%d\n",
|
---|
| 1901 | vrc, pvrcGuest ? *pvrcGuest : -VERR_IPE_UNINITIALIZED_STATUS, pProcessStatus ? *pProcessStatus : -1));
|
---|
[45415] | 1902 | return vrc;
|
---|
| 1903 | }
|
---|
| 1904 |
|
---|
[79283] | 1905 | #if 0 /* Unused */
|
---|
[47469] | 1906 | /* static */
|
---|
[77074] | 1907 | bool GuestProcess::i_waitResultImpliesEx(ProcessWaitResult_T waitResult, ProcessStatus_T procStatus, uint32_t uProtocol)
|
---|
[47469] | 1908 | {
|
---|
[77074] | 1909 | RT_NOREF(uProtocol);
|
---|
| 1910 |
|
---|
[47469] | 1911 | bool fImplies;
|
---|
| 1912 |
|
---|
| 1913 | switch (waitResult)
|
---|
| 1914 | {
|
---|
| 1915 | case ProcessWaitResult_Start:
|
---|
| 1916 | fImplies = procStatus == ProcessStatus_Started;
|
---|
| 1917 | break;
|
---|
| 1918 |
|
---|
| 1919 | case ProcessWaitResult_Terminate:
|
---|
| 1920 | fImplies = ( procStatus == ProcessStatus_TerminatedNormally
|
---|
| 1921 | || procStatus == ProcessStatus_TerminatedSignal
|
---|
| 1922 | || procStatus == ProcessStatus_TerminatedAbnormally
|
---|
| 1923 | || procStatus == ProcessStatus_TimedOutKilled
|
---|
| 1924 | || procStatus == ProcessStatus_TimedOutAbnormally
|
---|
| 1925 | || procStatus == ProcessStatus_Down
|
---|
| 1926 | || procStatus == ProcessStatus_Error);
|
---|
| 1927 | break;
|
---|
| 1928 |
|
---|
| 1929 | default:
|
---|
| 1930 | fImplies = false;
|
---|
| 1931 | break;
|
---|
| 1932 | }
|
---|
| 1933 |
|
---|
| 1934 | return fImplies;
|
---|
| 1935 | }
|
---|
[79283] | 1936 | #endif /* unused */
|
---|
[47469] | 1937 |
|
---|
[92897] | 1938 | /**
|
---|
| 1939 | * Writes input data to a guest process.
|
---|
| 1940 | *
|
---|
| 1941 | * @returns VBox status code.
|
---|
| 1942 | * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
|
---|
| 1943 | * @param uHandle Guest process file handle to write to.
|
---|
| 1944 | * @param uFlags Input flags of type PRocessInputFlag_XXX.
|
---|
| 1945 | * @param pvData Data to write to the guest process.
|
---|
| 1946 | * @param cbData Size (in bytes) of \a pvData to write.
|
---|
| 1947 | * @param uTimeoutMS Timeout (in ms) to wait.
|
---|
| 1948 | * @param puWritten Where to return the size (in bytes) written. Optional.
|
---|
[98272] | 1949 | * @param pvrcGuest Where to return the guest error when
|
---|
| 1950 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 1951 | *
|
---|
| 1952 | * @note Takes the write lock.
|
---|
| 1953 | */
|
---|
[50709] | 1954 | int GuestProcess::i_writeData(uint32_t uHandle, uint32_t uFlags,
|
---|
[98272] | 1955 | void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *pvrcGuest)
|
---|
[42160] | 1956 | {
|
---|
[98272] | 1957 | LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p, pvrcGuest=%p\n",
|
---|
| 1958 | mData.mPID, uHandle, uFlags, pvData, cbData, uTimeoutMS, puWritten, pvrcGuest));
|
---|
[42634] | 1959 | /* All is optional. There can be 0 byte writes. */
|
---|
[42507] | 1960 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
| 1961 |
|
---|
| 1962 | if (mData.mStatus != ProcessStatus_Started)
|
---|
[43162] | 1963 | {
|
---|
| 1964 | if (puWritten)
|
---|
| 1965 | *puWritten = 0;
|
---|
[98272] | 1966 | if (pvrcGuest)
|
---|
| 1967 | *pvrcGuest = VINF_SUCCESS;
|
---|
[42716] | 1968 | return VINF_SUCCESS; /* Not available for writing (anymore). */
|
---|
[43162] | 1969 | }
|
---|
[42507] | 1970 |
|
---|
[45780] | 1971 | int vrc;
|
---|
| 1972 |
|
---|
| 1973 | GuestWaitEvent *pEvent = NULL;
|
---|
[49349] | 1974 | GuestEventTypes eventTypes;
|
---|
[45780] | 1975 | try
|
---|
[43162] | 1976 | {
|
---|
[47469] | 1977 | /*
|
---|
| 1978 | * On Guest Additions < 4.3 there is no guarantee that the process status
|
---|
| 1979 | * change arrives *after* the input event, e.g. if this was the last input
|
---|
| 1980 | * block being written and the process will report status "terminate".
|
---|
| 1981 | * So just skip checking for process status change and only wait for the
|
---|
| 1982 | * input event.
|
---|
| 1983 | */
|
---|
[50727] | 1984 | if (mSession->i_getProtocolVersion() >= 2)
|
---|
[47469] | 1985 | eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
|
---|
[45780] | 1986 | eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
|
---|
[42507] | 1987 |
|
---|
[47469] | 1988 | vrc = registerWaitEvent(eventTypes, &pEvent);
|
---|
[45780] | 1989 | }
|
---|
[73505] | 1990 | catch (std::bad_alloc &)
|
---|
[45780] | 1991 | {
|
---|
| 1992 | vrc = VERR_NO_MEMORY;
|
---|
| 1993 | }
|
---|
[42507] | 1994 |
|
---|
[45780] | 1995 | if (RT_FAILURE(vrc))
|
---|
| 1996 | return vrc;
|
---|
[45697] | 1997 |
|
---|
[45780] | 1998 | VBOXHGCMSVCPARM paParms[5];
|
---|
| 1999 | int i = 0;
|
---|
[75737] | 2000 | HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
|
---|
| 2001 | HGCMSvcSetU32(&paParms[i++], mData.mPID);
|
---|
| 2002 | HGCMSvcSetU32(&paParms[i++], uFlags);
|
---|
| 2003 | HGCMSvcSetPv(&paParms[i++], pvData, (uint32_t)cbData);
|
---|
| 2004 | HGCMSvcSetU32(&paParms[i++], (uint32_t)cbData);
|
---|
[42507] | 2005 |
|
---|
[45780] | 2006 | alock.release(); /* Drop the write lock before sending. */
|
---|
| 2007 |
|
---|
[47469] | 2008 | uint32_t cbProcessed = 0;
|
---|
[76958] | 2009 | vrc = sendMessage(HOST_MSG_EXEC_SET_INPUT, i, paParms);
|
---|
[42721] | 2010 | if (RT_SUCCESS(vrc))
|
---|
[42507] | 2011 | {
|
---|
[45415] | 2012 | ProcessInputStatus_T inputStatus;
|
---|
[50709] | 2013 | vrc = i_waitForInputNotify(pEvent, uHandle, uTimeoutMS,
|
---|
| 2014 | &inputStatus, &cbProcessed);
|
---|
[45415] | 2015 | if (RT_SUCCESS(vrc))
|
---|
[42507] | 2016 | {
|
---|
[98272] | 2017 | /** @todo Set vrcGuest. */
|
---|
[42507] | 2018 |
|
---|
[45415] | 2019 | if (puWritten)
|
---|
| 2020 | *puWritten = cbProcessed;
|
---|
[42507] | 2021 | }
|
---|
[45415] | 2022 | /** @todo Error handling. */
|
---|
[42507] | 2023 | }
|
---|
| 2024 |
|
---|
[47469] | 2025 | unregisterWaitEvent(pEvent);
|
---|
[45780] | 2026 |
|
---|
[98272] | 2027 | LogFlowThisFunc(("Returning cbProcessed=%RU32, vrc=%Rrc\n", cbProcessed, vrc));
|
---|
[42721] | 2028 | return vrc;
|
---|
[42160] | 2029 | }
|
---|
| 2030 |
|
---|
[42084] | 2031 | // implementation of public methods
|
---|
| 2032 | /////////////////////////////////////////////////////////////////////////////
|
---|
| 2033 |
|
---|
[50709] | 2034 | HRESULT GuestProcess::read(ULONG aHandle, ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
|
---|
[42084] | 2035 | {
|
---|
[71560] | 2036 | AutoCaller autoCaller(this);
|
---|
[98262] | 2037 | if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
|
---|
[49440] | 2038 |
|
---|
[42818] | 2039 | if (aToRead == 0)
|
---|
[42509] | 2040 | return setError(E_INVALIDARG, tr("The size to read is zero"));
|
---|
[42214] | 2041 |
|
---|
[71560] | 2042 | LogFlowThisFuncEnter();
|
---|
| 2043 |
|
---|
[52934] | 2044 | aData.resize(aToRead);
|
---|
[42214] | 2045 |
|
---|
[94915] | 2046 | HRESULT hrc = S_OK;
|
---|
[43162] | 2047 |
|
---|
[79189] | 2048 | uint32_t cbRead;
|
---|
[94915] | 2049 | int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
|
---|
| 2050 | int vrc = i_readData(aHandle, aToRead, aTimeoutMS, &aData.front(), aToRead, &cbRead, &vrcGuest);
|
---|
[42721] | 2051 | if (RT_SUCCESS(vrc))
|
---|
[42461] | 2052 | {
|
---|
[52934] | 2053 | if (aData.size() != cbRead)
|
---|
| 2054 | aData.resize(cbRead);
|
---|
[42461] | 2055 | }
|
---|
[43162] | 2056 | else
|
---|
| 2057 | {
|
---|
[52934] | 2058 | aData.resize(0);
|
---|
| 2059 |
|
---|
[43162] | 2060 | switch (vrc)
|
---|
| 2061 | {
|
---|
[45078] | 2062 | case VERR_GSTCTL_GUEST_ERROR:
|
---|
[91503] | 2063 | {
|
---|
[94915] | 2064 | GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
|
---|
| 2065 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
|
---|
| 2066 | tr("Reading %RU32 bytes from guest process handle %RU32 failed: %s", "", aToRead),
|
---|
| 2067 | aToRead, aHandle, GuestBase::getErrorAsString(ge).c_str());
|
---|
[43162] | 2068 | break;
|
---|
[91503] | 2069 | }
|
---|
[43162] | 2070 | default:
|
---|
[94915] | 2071 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from guest process \"%s\" (PID %RU32) failed: %Rrc"),
|
---|
| 2072 | mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
|
---|
[43162] | 2073 | break;
|
---|
| 2074 | }
|
---|
| 2075 | }
|
---|
[42787] | 2076 |
|
---|
[98272] | 2077 | LogFlowThisFunc(("vrc=%Rrc, cbRead=%RU32\n", vrc, cbRead));
|
---|
[49349] | 2078 | LogFlowFuncLeaveRC(vrc);
|
---|
[94915] | 2079 | return hrc;
|
---|
[42084] | 2080 | }
|
---|
| 2081 |
|
---|
[50709] | 2082 | HRESULT GuestProcess::terminate()
|
---|
[42084] | 2083 | {
|
---|
[71560] | 2084 | AutoCaller autoCaller(this);
|
---|
[98262] | 2085 | if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
|
---|
[71560] | 2086 |
|
---|
| 2087 | LogFlowThisFuncEnter();
|
---|
| 2088 |
|
---|
[94915] | 2089 | HRESULT hrc = S_OK;
|
---|
[42846] | 2090 |
|
---|
[94915] | 2091 | int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
|
---|
[104003] | 2092 | int vrc = i_terminateProcess(GSTCTL_DEFAULT_TIMEOUT_MS, &vrcGuest);
|
---|
[93720] | 2093 |
|
---|
| 2094 | switch (vrc)
|
---|
[42846] | 2095 | {
|
---|
[93720] | 2096 | case VINF_SUCCESS:
|
---|
| 2097 | /* Nothing to do here, all good. */
|
---|
| 2098 | break;
|
---|
| 2099 |
|
---|
| 2100 | case VWRN_INVALID_STATE:
|
---|
[42846] | 2101 | {
|
---|
[94915] | 2102 | GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
|
---|
| 2103 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, VWRN_INVALID_STATE,
|
---|
| 2104 | tr("Guest process is not in '%s' state anymore (current is in '%s')"),
|
---|
| 2105 | GuestProcess::i_statusToString(ProcessStatus_Started).c_str(),
|
---|
| 2106 | GuestProcess::i_statusToString(i_getStatus()).c_str());
|
---|
[93720] | 2107 | break;
|
---|
| 2108 | }
|
---|
[42846] | 2109 |
|
---|
[93720] | 2110 | case VERR_GSTCTL_GUEST_ERROR:
|
---|
| 2111 | {
|
---|
[94915] | 2112 | GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
|
---|
| 2113 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Terminating guest process failed: %s"),
|
---|
| 2114 | GuestBase::getErrorAsString(ge).c_str());
|
---|
[93720] | 2115 | break;
|
---|
[42846] | 2116 | }
|
---|
[93720] | 2117 |
|
---|
| 2118 | case VERR_NOT_SUPPORTED:
|
---|
| 2119 | {
|
---|
[94915] | 2120 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
|
---|
| 2121 | tr("Terminating guest process \"%s\" (PID %RU32) not supported by installed Guest Additions"),
|
---|
| 2122 | mData.mProcess.mExecutable.c_str(), mData.mPID);
|
---|
[93720] | 2123 | break;
|
---|
| 2124 | }
|
---|
| 2125 |
|
---|
| 2126 | default:
|
---|
[94915] | 2127 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Terminating guest process \"%s\" (PID %RU32) failed: %Rrc"),
|
---|
| 2128 | mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
|
---|
[93720] | 2129 | break;
|
---|
[42846] | 2130 | }
|
---|
| 2131 |
|
---|
[93720] | 2132 | /* Note: Also could be VWRN_INVALID_STATE from i_terminateProcess().
|
---|
| 2133 | * In such a case we have to keep the process in our list in order to fullfill any upcoming responses / requests. */
|
---|
| 2134 | if (vrc == VINF_SUCCESS)
|
---|
[93036] | 2135 | {
|
---|
| 2136 | /* Remove process from guest session list. Now only API clients
|
---|
| 2137 | * still can hold references to it. */
|
---|
| 2138 | AssertPtr(mSession);
|
---|
[94915] | 2139 | int vrc2 = mSession->i_processUnregister(this);
|
---|
[93036] | 2140 | if (RT_SUCCESS(vrc))
|
---|
[94915] | 2141 | vrc = vrc2;
|
---|
[93036] | 2142 | }
|
---|
[42897] | 2143 |
|
---|
[49349] | 2144 | LogFlowFuncLeaveRC(vrc);
|
---|
[94915] | 2145 | return hrc;
|
---|
[42084] | 2146 | }
|
---|
| 2147 |
|
---|
[99782] | 2148 | /* Deprecated; use GuestProcess::waitForArray() instead. */
|
---|
[71560] | 2149 | HRESULT GuestProcess::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
|
---|
[42084] | 2150 | {
|
---|
[71560] | 2151 | AutoCaller autoCaller(this);
|
---|
[98262] | 2152 | if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
|
---|
[71560] | 2153 |
|
---|
| 2154 | LogFlowThisFuncEnter();
|
---|
| 2155 |
|
---|
[79369] | 2156 | /* Validate flags: */
|
---|
| 2157 | static ULONG const s_fValidFlags = ProcessWaitForFlag_None | ProcessWaitForFlag_Start | ProcessWaitForFlag_Terminate
|
---|
| 2158 | | ProcessWaitForFlag_StdIn | ProcessWaitForFlag_StdOut | ProcessWaitForFlag_StdErr;
|
---|
| 2159 | if (aWaitFor & ~s_fValidFlags)
|
---|
| 2160 | return setErrorBoth(E_INVALIDARG, VERR_INVALID_FLAGS, tr("Flags value %#x, invalid: %#x"),
|
---|
| 2161 | aWaitFor, aWaitFor & ~s_fValidFlags);
|
---|
| 2162 |
|
---|
[42272] | 2163 | /*
|
---|
| 2164 | * Note: Do not hold any locks here while waiting!
|
---|
| 2165 | */
|
---|
[94915] | 2166 | HRESULT hrc = S_OK;
|
---|
[42354] | 2167 |
|
---|
[94915] | 2168 | int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
|
---|
[55535] | 2169 | ProcessWaitResult_T waitResult;
|
---|
[94915] | 2170 | int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &vrcGuest);
|
---|
[42721] | 2171 | if (RT_SUCCESS(vrc))
|
---|
[42354] | 2172 | {
|
---|
[43162] | 2173 | *aReason = waitResult;
|
---|
[42354] | 2174 | }
|
---|
| 2175 | else
|
---|
[42436] | 2176 | {
|
---|
[43162] | 2177 | switch (vrc)
|
---|
| 2178 | {
|
---|
[45078] | 2179 | case VERR_GSTCTL_GUEST_ERROR:
|
---|
[91503] | 2180 | {
|
---|
[94915] | 2181 | GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
|
---|
| 2182 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Waiting for guest process (flags %#x) failed: %s"),
|
---|
| 2183 | aWaitFor, GuestBase::getErrorAsString(ge).c_str());
|
---|
[43162] | 2184 | break;
|
---|
[91503] | 2185 | }
|
---|
[43288] | 2186 | case VERR_TIMEOUT:
|
---|
| 2187 | *aReason = ProcessWaitResult_Timeout;
|
---|
| 2188 | break;
|
---|
| 2189 |
|
---|
[43162] | 2190 | default:
|
---|
[94915] | 2191 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Waiting for guest process \"%s\" (PID %RU32) failed: %Rrc"),
|
---|
| 2192 | mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
|
---|
[43162] | 2193 | break;
|
---|
| 2194 | }
|
---|
[42436] | 2195 | }
|
---|
[43162] | 2196 |
|
---|
[49349] | 2197 | LogFlowFuncLeaveRC(vrc);
|
---|
[94915] | 2198 | return hrc;
|
---|
[42084] | 2199 | }
|
---|
| 2200 |
|
---|
[50874] | 2201 | HRESULT GuestProcess::waitForArray(const std::vector<ProcessWaitForFlag_T> &aWaitFor,
|
---|
| 2202 | ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
|
---|
[42411] | 2203 | {
|
---|
| 2204 | uint32_t fWaitFor = ProcessWaitForFlag_None;
|
---|
[50709] | 2205 | for (size_t i = 0; i < aWaitFor.size(); i++)
|
---|
| 2206 | fWaitFor |= aWaitFor[i];
|
---|
[42411] | 2207 |
|
---|
[42461] | 2208 | return WaitFor(fWaitFor, aTimeoutMS, aReason);
|
---|
[42411] | 2209 | }
|
---|
| 2210 |
|
---|
[50709] | 2211 | HRESULT GuestProcess::write(ULONG aHandle, ULONG aFlags, const std::vector<BYTE> &aData,
|
---|
| 2212 | ULONG aTimeoutMS, ULONG *aWritten)
|
---|
[42084] | 2213 | {
|
---|
[79369] | 2214 | static ULONG const s_fValidFlags = ProcessInputFlag_None | ProcessInputFlag_EndOfFile;
|
---|
| 2215 | if (aFlags & ~s_fValidFlags)
|
---|
| 2216 | return setErrorBoth(E_INVALIDARG, VERR_INVALID_FLAGS, tr("Flags value %#x, invalid: %#x"),
|
---|
| 2217 | aFlags, aFlags & ~s_fValidFlags);
|
---|
| 2218 |
|
---|
[71560] | 2219 | AutoCaller autoCaller(this);
|
---|
[98262] | 2220 | if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
|
---|
[71560] | 2221 |
|
---|
[42214] | 2222 | LogFlowThisFuncEnter();
|
---|
| 2223 |
|
---|
[94915] | 2224 | HRESULT hrc = S_OK;
|
---|
[52934] | 2225 |
|
---|
[79189] | 2226 | uint32_t cbWritten;
|
---|
[94915] | 2227 | int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
|
---|
| 2228 | uint32_t cbData = (uint32_t)aData.size();
|
---|
| 2229 | void *pvData = cbData > 0 ? (void *)&aData.front() : NULL;
|
---|
| 2230 | int vrc = i_writeData(aHandle, aFlags, pvData, cbData, aTimeoutMS, &cbWritten, &vrcGuest);
|
---|
[43162] | 2231 | if (RT_FAILURE(vrc))
|
---|
| 2232 | {
|
---|
| 2233 | switch (vrc)
|
---|
| 2234 | {
|
---|
[45078] | 2235 | case VERR_GSTCTL_GUEST_ERROR:
|
---|
[91503] | 2236 | {
|
---|
[94915] | 2237 | GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, mData.mProcess.mExecutable.c_str());
|
---|
| 2238 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
|
---|
| 2239 | tr("Writing %RU32 bytes (flags %#x) to guest process failed: %s", "", cbData),
|
---|
| 2240 | cbData, aFlags, GuestBase::getErrorAsString(ge).c_str());
|
---|
[43162] | 2241 | break;
|
---|
[91503] | 2242 | }
|
---|
[43162] | 2243 | default:
|
---|
[94915] | 2244 | hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing to guest process \"%s\" (PID %RU32) failed: %Rrc"),
|
---|
| 2245 | mData.mProcess.mExecutable.c_str(), mData.mPID, vrc);
|
---|
[43162] | 2246 | break;
|
---|
| 2247 | }
|
---|
| 2248 | }
|
---|
| 2249 |
|
---|
[98272] | 2250 | LogFlowThisFunc(("vrc=%Rrc, aWritten=%RU32\n", vrc, cbWritten));
|
---|
[43162] | 2251 |
|
---|
[45697] | 2252 | *aWritten = (ULONG)cbWritten;
|
---|
| 2253 |
|
---|
[49349] | 2254 | LogFlowFuncLeaveRC(vrc);
|
---|
[94915] | 2255 | return hrc;
|
---|
[42084] | 2256 | }
|
---|
| 2257 |
|
---|
[50709] | 2258 | HRESULT GuestProcess::writeArray(ULONG aHandle, const std::vector<ProcessInputFlag_T> &aFlags,
|
---|
| 2259 | const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
|
---|
[42507] | 2260 | {
|
---|
| 2261 | LogFlowThisFuncEnter();
|
---|
| 2262 |
|
---|
| 2263 | ULONG fWrite = ProcessInputFlag_None;
|
---|
[50709] | 2264 | for (size_t i = 0; i < aFlags.size(); i++)
|
---|
| 2265 | fWrite |= aFlags[i];
|
---|
[42507] | 2266 |
|
---|
[50709] | 2267 | return write(aHandle, fWrite, aData, aTimeoutMS, aWritten);
|
---|
[42507] | 2268 | }
|
---|
| 2269 |
|
---|
[43162] | 2270 | ///////////////////////////////////////////////////////////////////////////////
|
---|
| 2271 |
|
---|
[104178] | 2272 | GuestProcessWrapper::GuestProcessWrapper(void)
|
---|
[49504] | 2273 | : pSession(NULL),
|
---|
| 2274 | pProcess(NULL)
|
---|
[43162] | 2275 | {
|
---|
| 2276 | }
|
---|
| 2277 |
|
---|
[104178] | 2278 | GuestProcessWrapper::~GuestProcessWrapper(void)
|
---|
[43162] | 2279 | {
|
---|
[71564] | 2280 | uninit();
|
---|
[43162] | 2281 | }
|
---|
| 2282 |
|
---|
[92897] | 2283 | /**
|
---|
[104178] | 2284 | * Initializes and starts a process on the guest.
|
---|
[92897] | 2285 | *
|
---|
| 2286 | * @returns VBox status code.
|
---|
[104178] | 2287 | * @param pGuestSession Guest session the process should be started in.
|
---|
[92897] | 2288 | * @param startupInfo Guest process startup info to use for starting.
|
---|
| 2289 | * @param fAsync Whether to start asynchronously or not.
|
---|
[98272] | 2290 | * @param pvrcGuest Where to return the guest error when
|
---|
| 2291 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 2292 | */
|
---|
[104178] | 2293 | int GuestProcessWrapper::init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
|
---|
[98526] | 2294 | bool fAsync, int *pvrcGuest)
|
---|
[43162] | 2295 | {
|
---|
[55535] | 2296 | LogFlowThisFunc(("pGuestSession=%p, exe=%s, fAsync=%RTbool\n",
|
---|
| 2297 | pGuestSession, startupInfo.mExecutable.c_str(), fAsync));
|
---|
[43162] | 2298 |
|
---|
| 2299 | AssertPtrReturn(pGuestSession, VERR_INVALID_POINTER);
|
---|
[56709] | 2300 | Assert(startupInfo.mArguments[0] == startupInfo.mExecutable);
|
---|
[43162] | 2301 |
|
---|
[49349] | 2302 | pSession = pGuestSession;
|
---|
[43162] | 2303 | mStartupInfo = startupInfo;
|
---|
| 2304 |
|
---|
| 2305 | /* Make sure the process is hidden. */
|
---|
| 2306 | mStartupInfo.mFlags |= ProcessCreateFlag_Hidden;
|
---|
| 2307 |
|
---|
[71345] | 2308 | int vrc = pSession->i_processCreateEx(mStartupInfo, pProcess);
|
---|
[43162] | 2309 | if (RT_SUCCESS(vrc))
|
---|
[60622] | 2310 | {
|
---|
[63155] | 2311 | int vrcGuest = VINF_SUCCESS;
|
---|
[49570] | 2312 | vrc = fAsync
|
---|
[50709] | 2313 | ? pProcess->i_startProcessAsync()
|
---|
[104003] | 2314 | : pProcess->i_startProcess(GSTCTL_DEFAULT_TIMEOUT_MS, &vrcGuest);
|
---|
[43162] | 2315 |
|
---|
[60622] | 2316 | if ( RT_SUCCESS(vrc)
|
---|
| 2317 | && !fAsync
|
---|
[63155] | 2318 | && RT_FAILURE(vrcGuest)
|
---|
[43162] | 2319 | )
|
---|
[60622] | 2320 | {
|
---|
| 2321 | vrc = VERR_GSTCTL_GUEST_ERROR;
|
---|
| 2322 | }
|
---|
[81553] | 2323 |
|
---|
[98272] | 2324 | if (pvrcGuest)
|
---|
| 2325 | *pvrcGuest = vrcGuest;
|
---|
[43162] | 2326 | }
|
---|
| 2327 |
|
---|
[49349] | 2328 | LogFlowFuncLeaveRC(vrc);
|
---|
[43162] | 2329 | return vrc;
|
---|
| 2330 | }
|
---|
| 2331 |
|
---|
[92897] | 2332 | /**
|
---|
[104178] | 2333 | * Unitializes a guest process by terminating it on the guest.
|
---|
[92897] | 2334 | */
|
---|
[104178] | 2335 | void GuestProcessWrapper::uninit(void)
|
---|
[71564] | 2336 | {
|
---|
[71653] | 2337 | /* Make sure the process is terminated and unregistered from the guest session. */
|
---|
[94915] | 2338 | int vrcGuestIgnored;
|
---|
[104003] | 2339 | terminate(GSTCTL_DEFAULT_TIMEOUT_MS, &vrcGuestIgnored);
|
---|
[71564] | 2340 |
|
---|
[71825] | 2341 | /* Unregister the process from the process (and the session's object) list. */
|
---|
| 2342 | if ( pSession
|
---|
| 2343 | && pProcess)
|
---|
| 2344 | pSession->i_processUnregister(pProcess);
|
---|
| 2345 |
|
---|
[71653] | 2346 | /* Release references. */
|
---|
| 2347 | pProcess.setNull();
|
---|
| 2348 | pSession.setNull();
|
---|
[71564] | 2349 | }
|
---|
| 2350 |
|
---|
[92897] | 2351 | /**
|
---|
[104178] | 2352 | * Returns whether a guest process is still running or not.
|
---|
[92897] | 2353 | *
|
---|
| 2354 | * @returns \c true if running, or \c false if not.
|
---|
| 2355 | */
|
---|
[104178] | 2356 | bool GuestProcessWrapper::isRunning(void)
|
---|
[43162] | 2357 | {
|
---|
[49653] | 2358 | AssertReturn(!pProcess.isNull(), false);
|
---|
[43162] | 2359 |
|
---|
| 2360 | ProcessStatus_T procStatus = ProcessStatus_Undefined;
|
---|
[98272] | 2361 | HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
|
---|
| 2362 | AssertComRC(hrc);
|
---|
[43162] | 2363 |
|
---|
[49504] | 2364 | if ( procStatus == ProcessStatus_Started
|
---|
| 2365 | || procStatus == ProcessStatus_Paused
|
---|
| 2366 | || procStatus == ProcessStatus_Terminating)
|
---|
[43162] | 2367 | {
|
---|
[49504] | 2368 | return true;
|
---|
[43162] | 2369 | }
|
---|
| 2370 |
|
---|
[49504] | 2371 | return false;
|
---|
[43162] | 2372 | }
|
---|
| 2373 |
|
---|
[71301] | 2374 | /**
|
---|
[104178] | 2375 | * Returns whether the guest process has been run correctly or not, based on it's internal process
|
---|
[73036] | 2376 | * status and reported exit status.
|
---|
| 2377 | *
|
---|
[104178] | 2378 | * @return @c true if the guest process has been run correctly (exit status 0), or @c false if some error
|
---|
[73036] | 2379 | * occurred (exit status <> 0 or wrong process state).
|
---|
| 2380 | */
|
---|
[104178] | 2381 | bool GuestProcessWrapper::isTerminatedOk(void)
|
---|
[73036] | 2382 | {
|
---|
[98608] | 2383 | return getTerminationStatus() == VINF_SUCCESS;
|
---|
[73036] | 2384 | }
|
---|
| 2385 |
|
---|
[60622] | 2386 |
|
---|
[55613] | 2387 | /**
|
---|
[104178] | 2388 | * Reports if the guest process has been run correctly.
|
---|
[55613] | 2389 | *
|
---|
[98614] | 2390 | * @retval VINF_SUCCESS if the process has stopped and the exit code is zero.
|
---|
| 2391 | * @retval VERR_GSTCTL_PROCESS_EXIT_CODE if the exit code is _not_ zero.
|
---|
| 2392 | * @retval VERR_GSTCTL_PROCESS_WRONG_STATE if still running.
|
---|
[60622] | 2393 | *
|
---|
[70388] | 2394 | * @param piExitCode Exit code of the tool. Optional.
|
---|
[60622] | 2395 | */
|
---|
[104178] | 2396 | int GuestProcessWrapper::getTerminationStatus(int32_t *piExitCode /* = NULL */)
|
---|
[43162] | 2397 | {
|
---|
| 2398 | Assert(!pProcess.isNull());
|
---|
[98614] | 2399 | AssertPtrNull(piExitCode);
|
---|
[43162] | 2400 |
|
---|
[49349] | 2401 | int vrc;
|
---|
[71272] | 2402 | if (!isRunning())
|
---|
[43162] | 2403 | {
|
---|
[70388] | 2404 | LONG iExitCode = -1;
|
---|
[98272] | 2405 | HRESULT hrc = pProcess->COMGETTER(ExitCode(&iExitCode));
|
---|
| 2406 | AssertComRC(hrc);
|
---|
[43162] | 2407 |
|
---|
[70388] | 2408 | if (piExitCode)
|
---|
| 2409 | *piExitCode = iExitCode;
|
---|
[43162] | 2410 |
|
---|
[98614] | 2411 | vrc = iExitCode == 0 ? VINF_SUCCESS : VERR_GSTCTL_PROCESS_EXIT_CODE;
|
---|
[43162] | 2412 | }
|
---|
[49349] | 2413 | else
|
---|
[98614] | 2414 | {
|
---|
| 2415 | if (piExitCode)
|
---|
| 2416 | *piExitCode = -1;
|
---|
[60622] | 2417 | vrc = VERR_GSTCTL_PROCESS_WRONG_STATE;
|
---|
[98614] | 2418 | }
|
---|
[43162] | 2419 |
|
---|
[49349] | 2420 | LogFlowFuncLeaveRC(vrc);
|
---|
| 2421 | return vrc;
|
---|
[43162] | 2422 | }
|
---|
| 2423 |
|
---|
[92897] | 2424 | /**
|
---|
[104178] | 2425 | * Terminates a guest process.
|
---|
[92897] | 2426 | *
|
---|
| 2427 | * @returns VBox status code.
|
---|
[104178] | 2428 | * @param uTimeoutMS Timeout (in ms) to wait.
|
---|
| 2429 | * @param pvrcGuest Where to return the guest error when
|
---|
| 2430 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
| 2431 | */
|
---|
| 2432 | int GuestProcessWrapper::terminate(uint32_t uTimeoutMS, int *pvrcGuest)
|
---|
| 2433 | {
|
---|
| 2434 | LogFlowThisFuncEnter();
|
---|
| 2435 |
|
---|
| 2436 | int vrc;
|
---|
| 2437 | if (!pProcess.isNull())
|
---|
| 2438 | vrc = pProcess->i_terminateProcess(uTimeoutMS, pvrcGuest);
|
---|
| 2439 | else
|
---|
| 2440 | vrc = VERR_NOT_FOUND;
|
---|
| 2441 |
|
---|
| 2442 | LogFlowFuncLeaveRC(vrc);
|
---|
| 2443 | return vrc;
|
---|
| 2444 | }
|
---|
| 2445 |
|
---|
| 2446 | ///////////////////////////////////////////////////////////////////////////////
|
---|
| 2447 |
|
---|
| 2448 | #ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
|
---|
| 2449 | GuestProcessToolbox::GuestProcessToolbox(void)
|
---|
| 2450 | {
|
---|
| 2451 | }
|
---|
| 2452 |
|
---|
| 2453 | GuestProcessToolbox::~GuestProcessToolbox(void)
|
---|
| 2454 | {
|
---|
| 2455 | }
|
---|
| 2456 |
|
---|
| 2457 | /**
|
---|
| 2458 | * Waits for a guest toolbox process.
|
---|
| 2459 | *
|
---|
| 2460 | * @returns VBox status code.
|
---|
[92897] | 2461 | * @param fToolWaitFlags Guest process tool wait flags to use for waiting.
|
---|
[98272] | 2462 | * @param pvrcGuest Where to return the guest error when
|
---|
| 2463 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 2464 | */
|
---|
[98526] | 2465 | int GuestProcessToolbox::wait(uint32_t fToolWaitFlags, int *pvrcGuest)
|
---|
[43162] | 2466 | {
|
---|
[98272] | 2467 | return waitEx(fToolWaitFlags, NULL /* pStrmBlkOut */, pvrcGuest);
|
---|
[43162] | 2468 | }
|
---|
| 2469 |
|
---|
[92897] | 2470 | /**
|
---|
[104178] | 2471 | * Waits for a guest toolbox process, also returning process output.
|
---|
[92897] | 2472 | *
|
---|
| 2473 | * @returns VBox status code.
|
---|
| 2474 | * @param fToolWaitFlags Guest process tool wait flags to use for waiting.
|
---|
[104178] | 2475 | * @param pStrmBlkOut Where to store the guest toolbox output.
|
---|
[98272] | 2476 | * @param pvrcGuest Where to return the guest error when
|
---|
| 2477 | * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
|
---|
[92897] | 2478 | */
|
---|
[98526] | 2479 | int GuestProcessToolbox::waitEx(uint32_t fToolWaitFlags, GuestToolboxStreamBlock *pStrmBlkOut, int *pvrcGuest)
|
---|
[43162] | 2480 | {
|
---|
[104178] | 2481 | LogFlowThisFunc(("fToolWaitFlags=0x%x, pStrmBlkOut=%p, pvrcGuest=%p\n", fToolWaitFlags, pStrmBlkOut, pvrcGuest));
|
---|
[43162] | 2482 |
|
---|
| 2483 | int vrc;
|
---|
[99392] | 2484 |
|
---|
| 2485 | /* Is the next block complete without waiting for new data from the guest? */
|
---|
[71263] | 2486 | if (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK)
|
---|
[43162] | 2487 | {
|
---|
[49349] | 2488 | AssertPtr(pStrmBlkOut);
|
---|
[92707] | 2489 | vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
|
---|
[99392] | 2490 | if ( RT_SUCCESS(vrc)
|
---|
| 2491 | && pStrmBlkOut->IsComplete())
|
---|
[43162] | 2492 | return vrc;
|
---|
[64532] | 2493 | /* else do the waiting below. */
|
---|
[43162] | 2494 | }
|
---|
| 2495 |
|
---|
| 2496 | /* Do the waiting. */
|
---|
[71263] | 2497 | uint32_t fProcWaitForFlags = ProcessWaitForFlag_Terminate;
|
---|
[43162] | 2498 | if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
|
---|
[71263] | 2499 | fProcWaitForFlags |= ProcessWaitForFlag_StdOut;
|
---|
[43162] | 2500 | if (mStartupInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
|
---|
[71263] | 2501 | fProcWaitForFlags |= ProcessWaitForFlag_StdErr;
|
---|
[43162] | 2502 |
|
---|
[47627] | 2503 | /** @todo Decrease timeout while running. */
|
---|
[49349] | 2504 | uint64_t u64StartMS = RTTimeMilliTS();
|
---|
[43162] | 2505 | uint32_t uTimeoutMS = mStartupInfo.mTimeoutMS;
|
---|
| 2506 |
|
---|
[63155] | 2507 | int vrcGuest = VINF_SUCCESS;
|
---|
[43162] | 2508 | bool fDone = false;
|
---|
| 2509 |
|
---|
[98614] | 2510 | BYTE abBuf[_64K];
|
---|
[45482] | 2511 | uint32_t cbRead;
|
---|
[43162] | 2512 |
|
---|
| 2513 | bool fHandleStdOut = false;
|
---|
| 2514 | bool fHandleStdErr = false;
|
---|
| 2515 |
|
---|
[49349] | 2516 | /**
|
---|
| 2517 | * Updates the elapsed time and checks if a
|
---|
| 2518 | * timeout happened, then breaking out of the loop.
|
---|
| 2519 | */
|
---|
| 2520 | #define UPDATE_AND_CHECK_ELAPSED_TIME() \
|
---|
| 2521 | u64ElapsedMS = RTTimeMilliTS() - u64StartMS; \
|
---|
| 2522 | if ( uTimeoutMS != RT_INDEFINITE_WAIT \
|
---|
| 2523 | && u64ElapsedMS >= uTimeoutMS) \
|
---|
| 2524 | { \
|
---|
| 2525 | vrc = VERR_TIMEOUT; \
|
---|
| 2526 | break; \
|
---|
| 2527 | }
|
---|
| 2528 |
|
---|
| 2529 | /**
|
---|
| 2530 | * Returns the remaining time (in ms).
|
---|
| 2531 | */
|
---|
[98614] | 2532 | #define GET_REMAINING_TIME (uTimeoutMS == RT_INDEFINITE_WAIT ? RT_INDEFINITE_WAIT : uTimeoutMS - (uint32_t)u64ElapsedMS)
|
---|
[49349] | 2533 |
|
---|
[63178] | 2534 | ProcessWaitResult_T waitRes = ProcessWaitResult_None;
|
---|
[43162] | 2535 | do
|
---|
| 2536 | {
|
---|
[49349] | 2537 | uint64_t u64ElapsedMS;
|
---|
| 2538 | UPDATE_AND_CHECK_ELAPSED_TIME();
|
---|
| 2539 |
|
---|
[71263] | 2540 | vrc = pProcess->i_waitFor(fProcWaitForFlags, GET_REMAINING_TIME, waitRes, &vrcGuest);
|
---|
[43162] | 2541 | if (RT_FAILURE(vrc))
|
---|
| 2542 | break;
|
---|
| 2543 |
|
---|
| 2544 | switch (waitRes)
|
---|
| 2545 | {
|
---|
[43201] | 2546 | case ProcessWaitResult_StdIn:
|
---|
[43301] | 2547 | vrc = VERR_NOT_IMPLEMENTED;
|
---|
| 2548 | break;
|
---|
[43201] | 2549 |
|
---|
[43162] | 2550 | case ProcessWaitResult_StdOut:
|
---|
| 2551 | fHandleStdOut = true;
|
---|
| 2552 | break;
|
---|
| 2553 |
|
---|
| 2554 | case ProcessWaitResult_StdErr:
|
---|
| 2555 | fHandleStdErr = true;
|
---|
| 2556 | break;
|
---|
| 2557 |
|
---|
| 2558 | case ProcessWaitResult_WaitFlagNotSupported:
|
---|
[71263] | 2559 | if (fProcWaitForFlags & ProcessWaitForFlag_StdOut)
|
---|
[43162] | 2560 | fHandleStdOut = true;
|
---|
[71263] | 2561 | if (fProcWaitForFlags & ProcessWaitForFlag_StdErr)
|
---|
[43162] | 2562 | fHandleStdErr = true;
|
---|
| 2563 | /* Since waiting for stdout / stderr is not supported by the guest,
|
---|
| 2564 | * wait a bit to not hog the CPU too much when polling for data. */
|
---|
[98272] | 2565 | RTThreadSleep(1); /* Optional, don't check vrc. */
|
---|
[43162] | 2566 | break;
|
---|
| 2567 |
|
---|
| 2568 | case ProcessWaitResult_Error:
|
---|
[45078] | 2569 | vrc = VERR_GSTCTL_GUEST_ERROR;
|
---|
[43299] | 2570 | break;
|
---|
| 2571 |
|
---|
[43162] | 2572 | case ProcessWaitResult_Terminate:
|
---|
| 2573 | fDone = true;
|
---|
| 2574 | break;
|
---|
| 2575 |
|
---|
[43299] | 2576 | case ProcessWaitResult_Timeout:
|
---|
| 2577 | vrc = VERR_TIMEOUT;
|
---|
| 2578 | break;
|
---|
| 2579 |
|
---|
[43301] | 2580 | case ProcessWaitResult_Start:
|
---|
| 2581 | case ProcessWaitResult_Status:
|
---|
| 2582 | /* Not used here, just skip. */
|
---|
| 2583 | break;
|
---|
| 2584 |
|
---|
[43162] | 2585 | default:
|
---|
[49570] | 2586 | AssertMsgFailed(("Unhandled process wait result %RU32\n", waitRes));
|
---|
[43162] | 2587 | break;
|
---|
| 2588 | }
|
---|
| 2589 |
|
---|
[49349] | 2590 | if (RT_FAILURE(vrc))
|
---|
| 2591 | break;
|
---|
| 2592 |
|
---|
[43162] | 2593 | if (fHandleStdOut)
|
---|
| 2594 | {
|
---|
[49349] | 2595 | UPDATE_AND_CHECK_ELAPSED_TIME();
|
---|
| 2596 |
|
---|
[47627] | 2597 | cbRead = 0;
|
---|
[98614] | 2598 | vrc = pProcess->i_readData(GUEST_PROC_OUT_H_STDOUT, sizeof(abBuf),
|
---|
[50709] | 2599 | GET_REMAINING_TIME,
|
---|
[98614] | 2600 | abBuf, sizeof(abBuf),
|
---|
[63155] | 2601 | &cbRead, &vrcGuest);
|
---|
[49349] | 2602 | if ( RT_FAILURE(vrc)
|
---|
| 2603 | || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
|
---|
[43162] | 2604 | break;
|
---|
| 2605 |
|
---|
| 2606 | if (cbRead)
|
---|
| 2607 | {
|
---|
[47627] | 2608 | LogFlowThisFunc(("Received %RU32 bytes from stdout\n", cbRead));
|
---|
[98614] | 2609 | vrc = mStdOut.AddData(abBuf, cbRead);
|
---|
[43162] | 2610 |
|
---|
| 2611 | if ( RT_SUCCESS(vrc)
|
---|
[71263] | 2612 | && (fToolWaitFlags & GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK))
|
---|
[43162] | 2613 | {
|
---|
[49349] | 2614 | AssertPtr(pStrmBlkOut);
|
---|
[92707] | 2615 | vrc = getCurrentBlock(GUEST_PROC_OUT_H_STDOUT, *pStrmBlkOut);
|
---|
[49349] | 2616 |
|
---|
| 2617 | /* When successful, break out of the loop because we're done
|
---|
| 2618 | * with reading the first stream block. */
|
---|
[43162] | 2619 | if (RT_SUCCESS(vrc))
|
---|
| 2620 | fDone = true;
|
---|
| 2621 | }
|
---|
| 2622 | }
|
---|
| 2623 |
|
---|
| 2624 | fHandleStdOut = false;
|
---|
| 2625 | }
|
---|
| 2626 |
|
---|
| 2627 | if (fHandleStdErr)
|
---|
| 2628 | {
|
---|
[49349] | 2629 | UPDATE_AND_CHECK_ELAPSED_TIME();
|
---|
| 2630 |
|
---|
[47627] | 2631 | cbRead = 0;
|
---|
[98614] | 2632 | vrc = pProcess->i_readData(GUEST_PROC_OUT_H_STDERR, sizeof(abBuf),
|
---|
[50709] | 2633 | GET_REMAINING_TIME,
|
---|
[98614] | 2634 | abBuf, sizeof(abBuf),
|
---|
[63155] | 2635 | &cbRead, &vrcGuest);
|
---|
[49349] | 2636 | if ( RT_FAILURE(vrc)
|
---|
| 2637 | || vrc == VWRN_GSTCTL_OBJECTSTATE_CHANGED)
|
---|
[43162] | 2638 | break;
|
---|
| 2639 |
|
---|
| 2640 | if (cbRead)
|
---|
| 2641 | {
|
---|
[47627] | 2642 | LogFlowThisFunc(("Received %RU32 bytes from stderr\n", cbRead));
|
---|
[98614] | 2643 | vrc = mStdErr.AddData(abBuf, cbRead);
|
---|
[43162] | 2644 | }
|
---|
| 2645 |
|
---|
| 2646 | fHandleStdErr = false;
|
---|
| 2647 | }
|
---|
| 2648 |
|
---|
| 2649 | } while (!fDone && RT_SUCCESS(vrc));
|
---|
| 2650 |
|
---|
[49349] | 2651 | #undef UPDATE_AND_CHECK_ELAPSED_TIME
|
---|
| 2652 | #undef GET_REMAINING_TIME
|
---|
| 2653 |
|
---|
[63155] | 2654 | if (RT_FAILURE(vrcGuest))
|
---|
[49504] | 2655 | vrc = VERR_GSTCTL_GUEST_ERROR;
|
---|
| 2656 |
|
---|
[98272] | 2657 | LogFlowThisFunc(("Loop ended with vrc=%Rrc, vrcGuest=%Rrc, waitRes=%RU32\n", vrc, vrcGuest, waitRes));
|
---|
| 2658 | if (pvrcGuest)
|
---|
| 2659 | *pvrcGuest = vrcGuest;
|
---|
[43162] | 2660 |
|
---|
[49349] | 2661 | LogFlowFuncLeaveRC(vrc);
|
---|
[43162] | 2662 | return vrc;
|
---|
| 2663 | }
|
---|
| 2664 |
|
---|
[92897] | 2665 | /**
|
---|
[104178] | 2666 | * Gets the current guest toolbox process stream block.
|
---|
[92897] | 2667 | *
|
---|
| 2668 | * @returns VBox status code.
|
---|
[104178] | 2669 | * @retval VINF_EOF if the stream reached its end.
|
---|
| 2670 | * @param uHandle Guest process file handle to get current block for.
|
---|
| 2671 | * @param strmBlock Where to return the stream block on success.
|
---|
[92897] | 2672 | */
|
---|
[104178] | 2673 | int GuestProcessToolbox::getCurrentBlock(uint32_t uHandle, GuestToolboxStreamBlock &strmBlock)
|
---|
[43162] | 2674 | {
|
---|
[104178] | 2675 | GuestToolboxStream *pStream = NULL;
|
---|
| 2676 | if (uHandle == GUEST_PROC_OUT_H_STDOUT)
|
---|
| 2677 | pStream = &mStdOut;
|
---|
| 2678 | else if (uHandle == GUEST_PROC_OUT_H_STDERR)
|
---|
| 2679 | pStream = &mStdErr;
|
---|
[43162] | 2680 |
|
---|
[104178] | 2681 | if (!pStream)
|
---|
| 2682 | return VERR_INVALID_PARAMETER;
|
---|
[43162] | 2683 |
|
---|
[104178] | 2684 | int vrc = pStream->ParseBlock(strmBlock);
|
---|
| 2685 |
|
---|
| 2686 | LogFlowThisFunc(("vrc=%Rrc, currently %RU64 pairs\n", vrc, strmBlock.GetCount()));
|
---|
[94915] | 2687 | return vrc;
|
---|
[43162] | 2688 | }
|
---|
| 2689 |
|
---|
[60622] | 2690 | /**
|
---|
[104178] | 2691 | * Returns the result code from an ended guest toolbox process.
|
---|
| 2692 | *
|
---|
| 2693 | * @returns Result code from guest toolbox process.
|
---|
| 2694 | */
|
---|
| 2695 | int GuestProcessToolbox::getRc(void) const
|
---|
| 2696 | {
|
---|
| 2697 | LONG exitCode = -1;
|
---|
| 2698 | HRESULT hrc = pProcess->COMGETTER(ExitCode(&exitCode));
|
---|
| 2699 | AssertComRC(hrc);
|
---|
| 2700 |
|
---|
| 2701 | return GuestProcessToolbox::exitCodeToRc(mStartupInfo, exitCode);
|
---|
| 2702 | }
|
---|
| 2703 |
|
---|
| 2704 | /**
|
---|
| 2705 | * Static helper function to start and wait for output of a certain toolbox tool.
|
---|
| 2706 | *
|
---|
| 2707 | * The optional @a pLstStdOutObjects argument enables the caller to retrieve
|
---|
| 2708 | * so-called parsable guest stream objects containing additional information on
|
---|
| 2709 | * a key value pair basis. Those objects are emitted on the guest side by the
|
---|
| 2710 | * tool to convey retrieve additional information, like names in a directory or
|
---|
| 2711 | * file statistics, to the host.
|
---|
| 2712 | *
|
---|
| 2713 | * @return IPRT status code.
|
---|
| 2714 | * @param pGuestSession Guest control session to use for starting the toolbox tool in.
|
---|
| 2715 | * @param startupInfo Startup information about the toolbox tool.
|
---|
| 2716 | * @param pvrcGuest Error code returned from the guest side if VERR_GSTCTL_GUEST_ERROR is returned.
|
---|
| 2717 | * @param pLstStdOutObjects Pointer to vector to receive the stdout
|
---|
| 2718 | * objects. Optional.
|
---|
| 2719 | * @param cStdOutObjectsToRead Number of parsable guest stream objects to
|
---|
| 2720 | * try receive and parse. Ignored when
|
---|
| 2721 | * pLstStdOutObjects is NULL. Optional.
|
---|
| 2722 | */
|
---|
| 2723 | /* static */
|
---|
| 2724 | int GuestProcessToolbox::runTool(GuestSession *pGuestSession,
|
---|
| 2725 | GuestProcessStartupInfo const &startupInfo,
|
---|
| 2726 | int *pvrcGuest,
|
---|
| 2727 | GuestCtrlStreamObjects *pLstStdOutObjects /* = NULL */,
|
---|
| 2728 | uint32_t cStdOutObjectsToRead /* = 1 */)
|
---|
| 2729 | {
|
---|
| 2730 | GuestProcessToolErrorInfo errorInfo = { VERR_IPE_UNINITIALIZED_STATUS, INT32_MAX };
|
---|
| 2731 | int vrc = GuestProcessToolbox::runToolWithErrorInfo(pGuestSession, startupInfo,
|
---|
| 2732 | errorInfo, pLstStdOutObjects, cStdOutObjectsToRead);
|
---|
| 2733 | if (RT_SUCCESS(vrc))
|
---|
| 2734 | {
|
---|
| 2735 | /* Translate exit statuses to VBox statuses according to the guest tool: */
|
---|
| 2736 | if (errorInfo.vrcGuest == VERR_GSTCTL_PROCESS_EXIT_CODE)
|
---|
| 2737 | {
|
---|
| 2738 | errorInfo.vrcGuest = GuestProcessToolbox::exitCodeToRc(startupInfo, errorInfo.iExitCode);
|
---|
| 2739 | vrc = VERR_GSTCTL_GUEST_ERROR;
|
---|
| 2740 | }
|
---|
| 2741 | /* In case of something else that may be relevant: */
|
---|
| 2742 | else if (errorInfo.vrcGuest == VERR_GSTCTL_PROCESS_WRONG_STATE /* possible getTerminationStatus result, see below */)
|
---|
| 2743 | vrc = VERR_GSTCTL_GUEST_ERROR;
|
---|
| 2744 | else
|
---|
| 2745 | AssertMsg(errorInfo.vrcGuest == VERR_IPE_UNINITIALIZED_STATUS || errorInfo.vrcGuest == VINF_SUCCESS,
|
---|
| 2746 | ("vrcGuest=%Rrc iExitCode=%d vrc=%Rrc\n", errorInfo.vrcGuest, errorInfo.iExitCode, vrc));
|
---|
| 2747 | }
|
---|
| 2748 |
|
---|
| 2749 | *pvrcGuest = errorInfo.vrcGuest;
|
---|
| 2750 | LogFlowFunc(("returns %Rrc - vrcGuest=%Rrc iExitCode=%d\n", vrc, errorInfo.vrcGuest, errorInfo.iExitCode));
|
---|
| 2751 | return vrc;
|
---|
| 2752 | }
|
---|
| 2753 |
|
---|
| 2754 | /**
|
---|
| 2755 | * Bare-bone version of runTool that doesn't translate exit codes to statuses.
|
---|
| 2756 | *
|
---|
| 2757 | * It's currently not used by anyone other than runTool().
|
---|
| 2758 | *
|
---|
| 2759 | * @return VBox status code.
|
---|
| 2760 | * @param pGuestSession Guest control session to use for starting the toolbox tool in.
|
---|
| 2761 | * @param startupInfo Startup information about the toolbox tool.
|
---|
| 2762 | * @param errorInfo Error information returned for error handling.
|
---|
| 2763 | * @param pLstStdOutObjects Pointer to vector to receive the stdout
|
---|
| 2764 | * objects. Optional.
|
---|
| 2765 | * @param cStdOutObjectsToRead Number of parsable guest stream objects to
|
---|
| 2766 | * try receive and parse. Ignored when
|
---|
| 2767 | * pLstStdOutObjects is NULL. Optional.
|
---|
| 2768 | *
|
---|
| 2769 | * @note This will not do call exitCodeToRc, for that use runTool().
|
---|
| 2770 | */
|
---|
| 2771 | /* static */
|
---|
| 2772 | int GuestProcessToolbox::runToolWithErrorInfo(GuestSession *pGuestSession,
|
---|
| 2773 | GuestProcessStartupInfo const &startupInfo,
|
---|
| 2774 | GuestProcessToolErrorInfo &errorInfo,
|
---|
| 2775 | GuestCtrlStreamObjects *pLstStdOutObjects /* = NULL */,
|
---|
| 2776 | uint32_t cStdOutObjectsToRead /* = 1 */)
|
---|
| 2777 | {
|
---|
| 2778 | GuestProcessToolbox procTool;
|
---|
| 2779 | int vrc = procTool.init(pGuestSession, startupInfo, false /* fAsync */, &errorInfo.vrcGuest);
|
---|
| 2780 | if (pLstStdOutObjects)
|
---|
| 2781 | while (RT_SUCCESS(vrc) && cStdOutObjectsToRead-- > 0)
|
---|
| 2782 | try
|
---|
| 2783 | {
|
---|
| 2784 | GuestToolboxStreamBlock strmBlk;
|
---|
| 2785 | vrc = procTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &strmBlk, &errorInfo.vrcGuest);
|
---|
| 2786 | pLstStdOutObjects->push_back(strmBlk);
|
---|
| 2787 | }
|
---|
| 2788 | catch (std::bad_alloc &)
|
---|
| 2789 | {
|
---|
| 2790 | vrc = VERR_NO_MEMORY;
|
---|
| 2791 | }
|
---|
| 2792 | if (RT_SUCCESS(vrc))
|
---|
| 2793 | {
|
---|
| 2794 | /* Make sure the process runs until completion. */
|
---|
| 2795 | vrc = procTool.wait(GUESTPROCESSTOOL_WAIT_FLAG_NONE, &errorInfo.vrcGuest);
|
---|
| 2796 | if (RT_SUCCESS(vrc))
|
---|
| 2797 | errorInfo.vrcGuest = procTool.getTerminationStatus(&errorInfo.iExitCode);
|
---|
| 2798 | }
|
---|
| 2799 |
|
---|
| 2800 | LogFlowFunc(("returns %Rrc - vrcGuest=%Rrc iExitCode=%d (%#x)\n",
|
---|
| 2801 | vrc, errorInfo.vrcGuest, errorInfo.iExitCode, errorInfo.iExitCode));
|
---|
| 2802 | return vrc;
|
---|
| 2803 | }
|
---|
| 2804 |
|
---|
| 2805 | /**
|
---|
[60622] | 2806 | * Converts a toolbox tool's exit code to an IPRT error code.
|
---|
| 2807 | *
|
---|
[92897] | 2808 | * @returns VBox status code.
|
---|
| 2809 | * @param startupInfo Startup info of the toolbox tool to lookup error code for.
|
---|
| 2810 | * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
|
---|
[60622] | 2811 | */
|
---|
| 2812 | /* static */
|
---|
[98526] | 2813 | int GuestProcessToolbox::exitCodeToRc(const GuestProcessStartupInfo &startupInfo, int32_t iExitCode)
|
---|
[60622] | 2814 | {
|
---|
| 2815 | if (startupInfo.mArguments.size() == 0)
|
---|
| 2816 | {
|
---|
| 2817 | AssertFailed();
|
---|
| 2818 | return VERR_GENERAL_FAILURE; /* Should not happen. */
|
---|
| 2819 | }
|
---|
| 2820 |
|
---|
[71272] | 2821 | return exitCodeToRc(startupInfo.mArguments[0].c_str(), iExitCode);
|
---|
[60622] | 2822 | }
|
---|
| 2823 |
|
---|
| 2824 | /**
|
---|
| 2825 | * Converts a toolbox tool's exit code to an IPRT error code.
|
---|
| 2826 | *
|
---|
[92897] | 2827 | * @returns VBox status code.
|
---|
| 2828 | * @param pszTool Name of toolbox tool to lookup error code for.
|
---|
| 2829 | * @param iExitCode The toolbox tool's exit code to lookup IPRT error for.
|
---|
[60622] | 2830 | */
|
---|
| 2831 | /* static */
|
---|
[98526] | 2832 | int GuestProcessToolbox::exitCodeToRc(const char *pszTool, int32_t iExitCode)
|
---|
[60622] | 2833 | {
|
---|
| 2834 | AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
|
---|
| 2835 |
|
---|
[70388] | 2836 | LogFlowFunc(("%s: %d\n", pszTool, iExitCode));
|
---|
[60622] | 2837 |
|
---|
[70388] | 2838 | if (iExitCode == 0) /* No error? Bail out early. */
|
---|
[60622] | 2839 | return VINF_SUCCESS;
|
---|
| 2840 |
|
---|
| 2841 | if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_CAT))
|
---|
| 2842 | {
|
---|
[70388] | 2843 | switch (iExitCode)
|
---|
[60622] | 2844 | {
|
---|
| 2845 | case VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
|
---|
| 2846 | case VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
|
---|
| 2847 | case VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
|
---|
| 2848 | case VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION: return VERR_SHARING_VIOLATION;
|
---|
[71176] | 2849 | case VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY: return VERR_IS_A_DIRECTORY;
|
---|
[71125] | 2850 | default: break;
|
---|
[60622] | 2851 | }
|
---|
| 2852 | }
|
---|
[84648] | 2853 | else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_LS))
|
---|
| 2854 | {
|
---|
| 2855 | switch (iExitCode)
|
---|
| 2856 | {
|
---|
| 2857 | /** @todo Handle access denied? */
|
---|
| 2858 | case RTEXITCODE_FAILURE: return VERR_PATH_NOT_FOUND;
|
---|
| 2859 | default: break;
|
---|
| 2860 | }
|
---|
| 2861 | }
|
---|
[60622] | 2862 | else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_STAT))
|
---|
| 2863 | {
|
---|
[70388] | 2864 | switch (iExitCode)
|
---|
[60622] | 2865 | {
|
---|
[71517] | 2866 | case VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED: return VERR_ACCESS_DENIED;
|
---|
| 2867 | case VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND: return VERR_FILE_NOT_FOUND;
|
---|
| 2868 | case VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND: return VERR_PATH_NOT_FOUND;
|
---|
| 2869 | case VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND: return VERR_NET_PATH_NOT_FOUND;
|
---|
| 2870 | default: break;
|
---|
[60622] | 2871 | }
|
---|
| 2872 | }
|
---|
[70388] | 2873 | else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKDIR))
|
---|
| 2874 | {
|
---|
| 2875 | switch (iExitCode)
|
---|
| 2876 | {
|
---|
[71125] | 2877 | case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
|
---|
| 2878 | default: break;
|
---|
[70388] | 2879 | }
|
---|
| 2880 | }
|
---|
[77496] | 2881 | else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_MKTEMP))
|
---|
| 2882 | {
|
---|
| 2883 | switch (iExitCode)
|
---|
| 2884 | {
|
---|
| 2885 | case RTEXITCODE_FAILURE: return VERR_CANT_CREATE;
|
---|
| 2886 | default: break;
|
---|
| 2887 | }
|
---|
| 2888 | }
|
---|
| 2889 | else if (!RTStrICmp(pszTool, VBOXSERVICE_TOOL_RM))
|
---|
| 2890 | {
|
---|
| 2891 | switch (iExitCode)
|
---|
| 2892 | {
|
---|
[84648] | 2893 | case RTEXITCODE_FAILURE: return VERR_FILE_NOT_FOUND;
|
---|
| 2894 | /** @todo RTPathRmCmd does not yet distinguish between not found and access denied yet. */
|
---|
[77496] | 2895 | default: break;
|
---|
| 2896 | }
|
---|
| 2897 | }
|
---|
[60622] | 2898 |
|
---|
[71518] | 2899 | LogFunc(("Warning: Exit code %d not handled for tool '%s', returning VERR_GENERAL_FAILURE\n", iExitCode, pszTool));
|
---|
| 2900 |
|
---|
[70388] | 2901 | if (iExitCode == RTEXITCODE_SYNTAX)
|
---|
| 2902 | return VERR_INTERNAL_ERROR_5;
|
---|
[60622] | 2903 | return VERR_GENERAL_FAILURE;
|
---|
| 2904 | }
|
---|
| 2905 |
|
---|
[92897] | 2906 | /**
|
---|
| 2907 | * Returns a stringyfied error of a guest process tool error.
|
---|
| 2908 | *
|
---|
| 2909 | * @returns Stringyfied error.
|
---|
| 2910 | * @param pszTool Toolbox tool name to get stringyfied error for.
|
---|
| 2911 | * @param guestErrorInfo Guest error info to get stringyfied error for.
|
---|
| 2912 | */
|
---|
[84648] | 2913 | /* static */
|
---|
[98526] | 2914 | Utf8Str GuestProcessToolbox::guestErrorToString(const char *pszTool, const GuestErrorInfo &guestErrorInfo)
|
---|
[84648] | 2915 | {
|
---|
| 2916 | Utf8Str strErr;
|
---|
| 2917 |
|
---|
| 2918 | /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
|
---|
[98272] | 2919 | switch (guestErrorInfo.getVrc())
|
---|
[84648] | 2920 | {
|
---|
| 2921 | case VERR_ACCESS_DENIED:
|
---|
[91518] | 2922 | strErr.printf(tr("Access to \"%s\" denied"), guestErrorInfo.getWhat().c_str());
|
---|
[84648] | 2923 | break;
|
---|
| 2924 |
|
---|
| 2925 | case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
|
---|
| 2926 | RT_FALL_THROUGH();
|
---|
| 2927 | case VERR_PATH_NOT_FOUND:
|
---|
[91518] | 2928 | strErr.printf(tr("No such file or directory \"%s\""), guestErrorInfo.getWhat().c_str());
|
---|
[84648] | 2929 | break;
|
---|
| 2930 |
|
---|
| 2931 | case VERR_INVALID_VM_HANDLE:
|
---|
[91518] | 2932 | strErr.printf(tr("VMM device is not available (is the VM running?)"));
|
---|
[84648] | 2933 | break;
|
---|
| 2934 |
|
---|
| 2935 | case VERR_HGCM_SERVICE_NOT_FOUND:
|
---|
[91518] | 2936 | strErr.printf(tr("The guest execution service is not available"));
|
---|
[84648] | 2937 | break;
|
---|
| 2938 |
|
---|
| 2939 | case VERR_BAD_EXE_FORMAT:
|
---|
[91518] | 2940 | strErr.printf(tr("The file \"%s\" is not an executable format"), guestErrorInfo.getWhat().c_str());
|
---|
[84648] | 2941 | break;
|
---|
| 2942 |
|
---|
| 2943 | case VERR_AUTHENTICATION_FAILURE:
|
---|
[91518] | 2944 | strErr.printf(tr("The user \"%s\" was not able to logon"), guestErrorInfo.getWhat().c_str());
|
---|
[84648] | 2945 | break;
|
---|
| 2946 |
|
---|
| 2947 | case VERR_INVALID_NAME:
|
---|
[91518] | 2948 | strErr.printf(tr("The file \"%s\" is an invalid name"), guestErrorInfo.getWhat().c_str());
|
---|
[84648] | 2949 | break;
|
---|
| 2950 |
|
---|
| 2951 | case VERR_TIMEOUT:
|
---|
[91518] | 2952 | strErr.printf(tr("The guest did not respond within time"));
|
---|
[84648] | 2953 | break;
|
---|
| 2954 |
|
---|
| 2955 | case VERR_CANCELLED:
|
---|
[91518] | 2956 | strErr.printf(tr("The execution operation was canceled"));
|
---|
[84648] | 2957 | break;
|
---|
| 2958 |
|
---|
| 2959 | case VERR_GSTCTL_MAX_CID_OBJECTS_REACHED:
|
---|
[91518] | 2960 | strErr.printf(tr("Maximum number of concurrent guest processes has been reached"));
|
---|
[84648] | 2961 | break;
|
---|
| 2962 |
|
---|
| 2963 | case VERR_NOT_FOUND:
|
---|
[91518] | 2964 | strErr.printf(tr("The guest execution service is not ready (yet)"));
|
---|
[84648] | 2965 | break;
|
---|
| 2966 |
|
---|
| 2967 | default:
|
---|
[91518] | 2968 | strErr.printf(tr("Unhandled error %Rrc for \"%s\" occurred for tool \"%s\" on guest -- please file a bug report"),
|
---|
[98272] | 2969 | guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str(), pszTool);
|
---|
[84648] | 2970 | break;
|
---|
| 2971 | }
|
---|
| 2972 |
|
---|
| 2973 | return strErr;
|
---|
| 2974 | }
|
---|
[104178] | 2975 | #endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
|
---|