summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/include/GuestProcessImpl.h
blob: 793b6db67a549b26e97f8bd04a9dfd1c4369de98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
/* $Id: GuestProcessImpl.h $ */
/** @file
 * VirtualBox Main - Guest process handling implementation.
 */

/*
 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
 *
 * This file is part of VirtualBox base platform packages, as
 * available from https://www.virtualbox.org.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, in version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <https://www.gnu.org/licenses>.
 *
 * SPDX-License-Identifier: GPL-3.0-only
 */

#ifndef MAIN_INCLUDED_GuestProcessImpl_h
#define MAIN_INCLUDED_GuestProcessImpl_h
#ifndef RT_WITHOUT_PRAGMA_ONCE
# pragma once
#endif

#include "GuestCtrlImplPrivate.h"
#include "GuestProcessWrap.h"

#include <iprt/cpp/utils.h>

class Console;
class GuestSession;
class GuestProcessStartTask;

/**
 * Class for handling a guest process.
 */
class ATL_NO_VTABLE GuestProcess :
    public GuestProcessWrap,
    public GuestObject
{
public:
    /** @name COM and internal init/term/mapping cruft.
     * @{ */
    DECLARE_COMMON_CLASS_METHODS(GuestProcess)

    int     init(Console *aConsole, GuestSession *aSession, ULONG aObjectID,
                 const GuestProcessStartupInfo &aProcInfo, const GuestEnvironment *pBaseEnv);
    void    uninit(void);
    HRESULT FinalConstruct(void);
    void    FinalRelease(void);
    /** @}  */

public:
    /** @name Implemented virtual methods from GuestObject.
     * @{ */
    int i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb);
    int i_onUnregister(void);
    int i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus);
    /** @}  */

public:
    /** @name Public internal methods.
     * @{ */
    inline int i_checkPID(uint32_t uPID);
    ProcessStatus_T i_getStatus(void);
    int i_readData(uint32_t uHandle, uint32_t uSize, uint32_t uTimeoutMS, void *pvData, size_t cbData, uint32_t *pcbRead, int *pvrcGuest);
    int i_startProcess(uint32_t cMsTimeout, int *pvrcGuest);
    int i_startProcessInner(uint32_t cMsTimeout, AutoWriteLock &rLock, GuestWaitEvent *pEvent, int *pvrcGuest);
    int i_startProcessAsync(void);
    int i_terminateProcess(uint32_t uTimeoutMS, int *pvrcGuest);
    ProcessWaitResult_T i_waitFlagsToResult(uint32_t fWaitFlags);
    int i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, ProcessWaitResult_T &waitResult, int *pvrcGuest);
    int i_waitForInputNotify(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS, ProcessInputStatus_T *pInputStatus, uint32_t *pcbProcessed);
    int i_waitForOutput(GuestWaitEvent *pEvent, uint32_t uHandle, uint32_t uTimeoutMS, void* pvData, size_t cbData, uint32_t *pcbRead);
    int i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS, ProcessStatus_T *pProcessStatus, int *pvrcGuest);
    int i_writeData(uint32_t uHandle, uint32_t uFlags, void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *pvrcGuest);
    /** @}  */

    /** @name Static internal methods.
     * @{ */
    static Utf8Str i_guestErrorToString(int vrcGuest, const char *pcszWhat);
    static Utf8Str i_statusToString(ProcessStatus_T enmStatus);
    static bool i_isGuestError(int guestRc);
    static ProcessWaitResult_T i_waitFlagsToResultEx(uint32_t fWaitFlags, ProcessStatus_T oldStatus, ProcessStatus_T newStatus, uint32_t uProcFlags, uint32_t uProtocol);
#if 0 /* unused */
    static bool i_waitResultImpliesEx(ProcessWaitResult_T waitResult, ProcessStatus_T procStatus, uint32_t uProtocol);
#endif
    /** @}  */

protected:
    /** @name Protected internal methods.
     * @{ */
    inline bool i_isAlive(void);
    inline bool i_hasEnded(void);
    int i_onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
    int i_onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
    int i_onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
    int i_onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
    int i_onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
    int i_prepareExecuteEnv(const char *pszEnv, void **ppvList, ULONG *pcbList, ULONG *pcEnvVars);
    int i_setProcessStatus(ProcessStatus_T procStatus, int vrcProc);
    static int i_startProcessThreadTask(GuestProcessStartTask *pTask);
    /** @}  */

private:
    /** Wrapped @name IProcess properties.
     * @{ */
    HRESULT getArguments(std::vector<com::Utf8Str> &aArguments);
    HRESULT getEnvironment(std::vector<com::Utf8Str> &aEnvironment);
    HRESULT getEventSource(ComPtr<IEventSource> &aEventSource);
    HRESULT getExecutablePath(com::Utf8Str &aExecutablePath);
    HRESULT getExitCode(LONG *aExitCode);
    HRESULT getName(com::Utf8Str &aName);
    HRESULT getPID(ULONG *aPID);
    HRESULT getStatus(ProcessStatus_T *aStatus);
    /** @}  */

    /** Wrapped @name IProcess methods.
     * @{ */
    HRESULT waitFor(ULONG aWaitFor,
                    ULONG aTimeoutMS,
                    ProcessWaitResult_T *aReason);
    HRESULT waitForArray(const std::vector<ProcessWaitForFlag_T> &aWaitFor,
                         ULONG aTimeoutMS,
                         ProcessWaitResult_T *aReason);
    HRESULT read(ULONG aHandle,
                 ULONG aToRead,
                 ULONG aTimeoutMS,
                 std::vector<BYTE> &aData);
    HRESULT write(ULONG aHandle,
                  ULONG aFlags,
                  const std::vector<BYTE> &aData,
                  ULONG aTimeoutMS,
                  ULONG *aWritten);
    HRESULT writeArray(ULONG aHandle,
                       const std::vector<ProcessInputFlag_T> &aFlags,
                       const std::vector<BYTE> &aData,
                       ULONG aTimeoutMS,
                       ULONG *aWritten);
    HRESULT terminate(void);
    /** @}  */

    /**
     * This can safely be used without holding any locks.
     * An AutoCaller suffices to prevent it being destroy while in use and
     * internally there is a lock providing the necessary serialization.
     */
    const ComObjPtr<EventSource> mEventSource;

    struct Data
    {
        /** The process startup information. */
        GuestProcessStartupInfo  mProcess;
        /** Reference to the immutable session base environment. NULL if the
         * environment feature isn't supported.
         * @remarks If there is proof that the uninit order of GuestSession and
         *          this class is what GuestObjectBase claims, then this isn't
         *          strictly necessary. */
        GuestEnvironment const  *mpSessionBaseEnv;
        /** Exit code if process has been terminated. */
        LONG                     mExitCode;
        /** PID reported from the guest.
         *  Note: This is *not* the internal object ID! */
        ULONG                    mPID;
        /** The current process status. */
        ProcessStatus_T          mStatus;
        /** The last returned process status
         *  returned from the guest side. */
        int                      mLastError;

        Data(void) : mpSessionBaseEnv(NULL)
        { }
        ~Data(void)
        {
            if (mpSessionBaseEnv)
            {
                mpSessionBaseEnv->releaseConst();
                mpSessionBaseEnv = NULL;
            }
        }
    } mData;

    friend class GuestProcessStartTask;
};

/**
 * Guest process tool wait flags.
 */
/** No wait flags specified; wait until process terminates.
 *  The maximum waiting time is set in the process' startup
 *  info. */
#define GUESTPROCESSTOOL_WAIT_FLAG_NONE            0
/** Wait until next stream block from stdout has been
 *  read in completely, then return.
 */
#define GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK    RT_BIT(0)

/**
 * Structure for keeping a VBoxService toolbox tool's error info around.
 */
struct GuestProcessToolErrorInfo
{
    /** Return (VBox status) code from the guest side for executing the process tool. */
    int     vrcGuest;
    /** The process tool's returned exit code. */
    int32_t iExitCode;
};

/**
 * Internal class for handling the BusyBox-like tools built into VBoxService
 * on the guest side. It's also called the VBoxService Toolbox (tm).
 *
 * Those initially were necessary to guarantee execution of commands (like "ls", "cat")
 * under the behalf of a certain guest user.
 *
 * This class essentially helps to wrap all the gory details like process creation,
 * information extraction and maintaining the overall status.
 *
 * Note! When implementing new functionality / commands, do *not* use this approach anymore!
 *       This class has to be kept to guarantee backwards-compatibility.
 */
class GuestProcessTool
{
public:
    DECLARE_TRANSLATE_METHODS(GuestProcessTool)

    GuestProcessTool(void);

    virtual ~GuestProcessTool(void);

public:

    int init(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo, bool fAsync, int *pvrcGuest);

    void uninit(void);

    int getCurrentBlock(uint32_t uHandle, GuestProcessStreamBlock &strmBlock);

    int getRc(void) const;

    /** Returns the stdout output from the guest process tool. */
    GuestProcessStream &getStdOut(void) { return mStdOut; }

    /** Returns the stderr output from the guest process tool. */
    GuestProcessStream &getStdErr(void) { return mStdErr; }

    int wait(uint32_t fToolWaitFlags, int *pvrcGuest);

    int waitEx(uint32_t fToolWaitFlags, GuestProcessStreamBlock *pStreamBlock, int *pvrcGuest);

    bool isRunning(void);

    bool isTerminatedOk(void);

    int getTerminationStatus(int32_t *piExitCode = NULL);

    int terminate(uint32_t uTimeoutMS, int *pvrcGuest);

public:

    /** Wrapped @name Static run methods.
     * @{ */
    static int run(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo, int *pvrcGuest);

    static int runErrorInfo(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo, GuestProcessToolErrorInfo &errorInfo);

    static int runEx(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
                     GuestCtrlStreamObjects *pStrmOutObjects, uint32_t cStrmOutObjects, int *pvrcGuest);

    static int runExErrorInfo(GuestSession *pGuestSession, const GuestProcessStartupInfo &startupInfo,
                              GuestCtrlStreamObjects *pStrmOutObjects, uint32_t cStrmOutObjects, GuestProcessToolErrorInfo &errorInfo);
    /** @}  */

    /** Wrapped @name Static exit code conversion methods.
     * @{ */
    static int exitCodeToRc(const GuestProcessStartupInfo &startupInfo, int32_t iExitCode);

    static int exitCodeToRc(const char *pszTool, int32_t iExitCode);
    /** @}  */

    /** Wrapped @name Static guest error conversion methods.
     * @{ */
    static Utf8Str guestErrorToString(const char *pszTool, const GuestErrorInfo& guestErrorInfo);
    /** @}  */

protected:

    /** Pointer to session this toolbox object is bound to. */
    ComObjPtr<GuestSession>     pSession;
    /** Pointer to process object this toolbox object is bound to. */
    ComObjPtr<GuestProcess>     pProcess;
    /** The toolbox' startup info. */
    GuestProcessStartupInfo     mStartupInfo;
    /** Stream object for handling the toolbox' stdout data. */
    GuestProcessStream          mStdOut;
    /** Stream object for handling the toolbox' stderr data. */
    GuestProcessStream          mStdErr;
};

#endif /* !MAIN_INCLUDED_GuestProcessImpl_h */