[27865] | 1 | /* $Id: VBoxServiceControl.cpp 102831 2024-01-11 08:56:53Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VBoxServiceControl - Host-driven Guest Control.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2012-2023 Oracle and/or its affiliates.
|
---|
[27865] | 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
|
---|
[27865] | 26 | */
|
---|
| 27 |
|
---|
[58033] | 28 | /** @page pg_vgsvc_gstctrl VBoxService - Guest Control
|
---|
| 29 | *
|
---|
| 30 | * The Guest Control subservice helps implementing the IGuest APIs.
|
---|
| 31 | *
|
---|
[58052] | 32 | * The communication between this service (and its children) and IGuest goes
|
---|
| 33 | * over the HGCM GuestControl service.
|
---|
| 34 | *
|
---|
[58033] | 35 | * The IGuest APIs provides means to manipulate (control) files, directories,
|
---|
| 36 | * symbolic links and processes within the guest. Most of these means requires
|
---|
| 37 | * credentials of a guest OS user to operate, though some restricted ones
|
---|
| 38 | * operates directly as the VBoxService user (root / system service account).
|
---|
| 39 | *
|
---|
| 40 | * The current design is that a subprocess is spawned for handling operations as
|
---|
| 41 | * a given user. This process is represented as IGuestSession in the API. The
|
---|
| 42 | * subprocess will be spawned as the given use, giving up the privileges the
|
---|
| 43 | * parent subservice had.
|
---|
| 44 | *
|
---|
| 45 | * It will try handle as many of the operations directly from within the
|
---|
| 46 | * subprocess, but for more complicated things (or things that haven't yet been
|
---|
| 47 | * converted), it will spawn a helper process that does the actual work.
|
---|
| 48 | *
|
---|
| 49 | * These helpers are the typically modeled on similar unix core utilities, like
|
---|
| 50 | * mkdir, rm, rmdir, cat and so on. The helper tools can also be launched
|
---|
| 51 | * directly from VBoxManage by the user by prepending the 'vbox_' prefix to the
|
---|
| 52 | * unix command.
|
---|
| 53 | *
|
---|
| 54 | */
|
---|
[27865] | 55 |
|
---|
[58033] | 56 |
|
---|
[57358] | 57 | /*********************************************************************************************************************************
|
---|
| 58 | * Header Files *
|
---|
| 59 | *********************************************************************************************************************************/
|
---|
[38157] | 60 | #include <iprt/asm.h>
|
---|
[27865] | 61 | #include <iprt/assert.h>
|
---|
[44863] | 62 | #include <iprt/env.h>
|
---|
[40059] | 63 | #include <iprt/file.h>
|
---|
[27926] | 64 | #include <iprt/getopt.h>
|
---|
[27865] | 65 | #include <iprt/mem.h>
|
---|
[40059] | 66 | #include <iprt/path.h>
|
---|
[44863] | 67 | #include <iprt/process.h>
|
---|
[27865] | 68 | #include <iprt/semaphore.h>
|
---|
| 69 | #include <iprt/thread.h>
|
---|
[76419] | 70 | #include <VBox/err.h>
|
---|
[27865] | 71 | #include <VBox/VBoxGuestLib.h>
|
---|
| 72 | #include <VBox/HostServices/GuestControlSvc.h>
|
---|
| 73 | #include "VBoxServiceInternal.h"
|
---|
[44863] | 74 | #include "VBoxServiceControl.h"
|
---|
[27865] | 75 | #include "VBoxServiceUtils.h"
|
---|
| 76 |
|
---|
| 77 | using namespace guestControl;
|
---|
| 78 |
|
---|
[57358] | 79 |
|
---|
| 80 | /*********************************************************************************************************************************
|
---|
| 81 | * Global Variables *
|
---|
| 82 | *********************************************************************************************************************************/
|
---|
[33540] | 83 | /** The control interval (milliseconds). */
|
---|
[58029] | 84 | static uint32_t g_msControlInterval = 0;
|
---|
[39279] | 85 | /** The semaphore we're blocking our main control thread on. */
|
---|
[39427] | 86 | static RTSEMEVENTMULTI g_hControlEvent = NIL_RTSEMEVENTMULTI;
|
---|
[44863] | 87 | /** The VM session ID. Changes whenever the VM is restored or reset. */
|
---|
| 88 | static uint64_t g_idControlSession;
|
---|
[38157] | 89 | /** The guest control service client ID. */
|
---|
[75807] | 90 | uint32_t g_idControlSvcClient = 0;
|
---|
[79296] | 91 | /** VBOX_GUESTCTRL_HF_XXX */
|
---|
| 92 | uint64_t g_fControlHostFeatures0 = 0;
|
---|
[62882] | 93 | #if 0 /** @todo process limit */
|
---|
[38157] | 94 | /** How many started guest processes are kept into memory for supplying
|
---|
[42738] | 95 | * information to the host. Default is 256 processes. If 0 is specified,
|
---|
[38157] | 96 | * the maximum number of processes is unlimited. */
|
---|
[42738] | 97 | static uint32_t g_uControlProcsMaxKept = 256;
|
---|
[62882] | 98 | #endif
|
---|
[44963] | 99 | /** List of guest control session threads (VBOXSERVICECTRLSESSIONTHREAD).
|
---|
| 100 | * A guest session thread represents a forked guest session process
|
---|
| 101 | * of VBoxService. */
|
---|
| 102 | RTLISTANCHOR g_lstControlSessionThreads;
|
---|
| 103 | /** The local session object used for handling all session-related stuff.
|
---|
| 104 | * When using the legacy guest control protocol (< 2), this session runs
|
---|
| 105 | * under behalf of the VBoxService main process. On newer protocol versions
|
---|
| 106 | * each session is a forked version of VBoxService using the appropriate
|
---|
[45010] | 107 | * user credentials for opening a guest session. These forked sessions then
|
---|
| 108 | * are kept in VBOXSERVICECTRLSESSIONTHREAD structures. */
|
---|
[44963] | 109 | VBOXSERVICECTRLSESSION g_Session;
|
---|
[75807] | 110 | /** Copy of VbglR3GuestCtrlSupportsOptimizations().*/
|
---|
| 111 | bool g_fControlSupportsOptimizations = true;
|
---|
[27865] | 112 |
|
---|
[57358] | 113 |
|
---|
| 114 | /*********************************************************************************************************************************
|
---|
| 115 | * Internal Functions *
|
---|
| 116 | *********************************************************************************************************************************/
|
---|
[58029] | 117 | static int vgsvcGstCtrlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx);
|
---|
| 118 | static int vgsvcGstCtrlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx);
|
---|
[84209] | 119 | static int vgsvcGstCtrlInvalidate(void);
|
---|
[58029] | 120 | static void vgsvcGstCtrlShutdown(void);
|
---|
[39279] | 121 |
|
---|
[39427] | 122 |
|
---|
[58029] | 123 | /**
|
---|
| 124 | * @interface_method_impl{VBOXSERVICE,pfnPreInit}
|
---|
| 125 | */
|
---|
| 126 | static DECLCALLBACK(int) vgsvcGstCtrlPreInit(void)
|
---|
[27865] | 127 | {
|
---|
[47551] | 128 | int rc;
|
---|
[38157] | 129 | #ifdef VBOX_WITH_GUEST_PROPS
|
---|
| 130 | /*
|
---|
| 131 | * Read the service options from the VM's guest properties.
|
---|
| 132 | * Note that these options can be overridden by the command line options later.
|
---|
| 133 | */
|
---|
| 134 | uint32_t uGuestPropSvcClientID;
|
---|
[47551] | 135 | rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
|
---|
[38157] | 136 | if (RT_FAILURE(rc))
|
---|
| 137 | {
|
---|
| 138 | if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
|
---|
| 139 | {
|
---|
[58029] | 140 | VGSvcVerbose(0, "Guest property service is not available, skipping\n");
|
---|
[38157] | 141 | rc = VINF_SUCCESS;
|
---|
| 142 | }
|
---|
| 143 | else
|
---|
[58029] | 144 | VGSvcError("Failed to connect to the guest property service, rc=%Rrc\n", rc);
|
---|
[38157] | 145 | }
|
---|
| 146 | else
|
---|
| 147 | VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
|
---|
| 148 |
|
---|
| 149 | if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
|
---|
| 150 | rc = VINF_SUCCESS;
|
---|
| 151 | #else
|
---|
| 152 | /* Nothing to do here yet. */
|
---|
[47551] | 153 | rc = VINF_SUCCESS;
|
---|
[38157] | 154 | #endif
|
---|
[47551] | 155 |
|
---|
| 156 | if (RT_SUCCESS(rc))
|
---|
| 157 | {
|
---|
| 158 | /* Init session object. */
|
---|
[58029] | 159 | rc = VGSvcGstCtrlSessionInit(&g_Session, 0 /* Flags */);
|
---|
[47551] | 160 | }
|
---|
| 161 |
|
---|
| 162 | return rc;
|
---|
[27865] | 163 | }
|
---|
| 164 |
|
---|
| 165 |
|
---|
[58029] | 166 | /**
|
---|
| 167 | * @interface_method_impl{VBOXSERVICE,pfnOption}
|
---|
| 168 | */
|
---|
| 169 | static DECLCALLBACK(int) vgsvcGstCtrlOption(const char **ppszShort, int argc, char **argv, int *pi)
|
---|
[27865] | 170 | {
|
---|
| 171 | int rc = -1;
|
---|
| 172 | if (ppszShort)
|
---|
| 173 | /* no short options */;
|
---|
| 174 | else if (!strcmp(argv[*pi], "--control-interval"))
|
---|
[58029] | 175 | rc = VGSvcArgUInt32(argc, argv, "", pi,
|
---|
| 176 | &g_msControlInterval, 1, UINT32_MAX - 1);
|
---|
[40059] | 177 | #ifdef DEBUG
|
---|
[44963] | 178 | else if (!strcmp(argv[*pi], "--control-dump-stdout"))
|
---|
[40059] | 179 | {
|
---|
[57659] | 180 | g_Session.fFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT;
|
---|
[40059] | 181 | rc = 0; /* Flag this command as parsed. */
|
---|
| 182 | }
|
---|
[44963] | 183 | else if (!strcmp(argv[*pi], "--control-dump-stderr"))
|
---|
[40059] | 184 | {
|
---|
[57659] | 185 | g_Session.fFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR;
|
---|
[40059] | 186 | rc = 0; /* Flag this command as parsed. */
|
---|
| 187 | }
|
---|
| 188 | #endif
|
---|
[27865] | 189 | return rc;
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 |
|
---|
[58029] | 193 | /**
|
---|
| 194 | * @interface_method_impl{VBOXSERVICE,pfnInit}
|
---|
| 195 | */
|
---|
| 196 | static DECLCALLBACK(int) vgsvcGstCtrlInit(void)
|
---|
[27865] | 197 | {
|
---|
| 198 | /*
|
---|
| 199 | * If not specified, find the right interval default.
|
---|
| 200 | * Then create the event sem to block on.
|
---|
| 201 | */
|
---|
[58029] | 202 | if (!g_msControlInterval)
|
---|
| 203 | g_msControlInterval = 1000;
|
---|
[27865] | 204 |
|
---|
| 205 | int rc = RTSemEventMultiCreate(&g_hControlEvent);
|
---|
| 206 | AssertRCReturn(rc, rc);
|
---|
| 207 |
|
---|
[75807] | 208 | VbglR3GetSessionId(&g_idControlSession); /* The status code is ignored as this information is not available with VBox < 3.2.10. */
|
---|
[44863] | 209 |
|
---|
[75807] | 210 | RTListInit(&g_lstControlSessionThreads);
|
---|
| 211 |
|
---|
| 212 | /*
|
---|
| 213 | * Try connect to the host service and tell it we want to be master (if supported).
|
---|
| 214 | */
|
---|
| 215 | rc = VbglR3GuestCtrlConnect(&g_idControlSvcClient);
|
---|
[27865] | 216 | if (RT_SUCCESS(rc))
|
---|
[29202] | 217 | {
|
---|
[84209] | 218 | rc = vgsvcGstCtrlInvalidate();
|
---|
[75807] | 219 | if (RT_SUCCESS(rc))
|
---|
[84209] | 220 | return rc;
|
---|
[29202] | 221 | }
|
---|
[27865] | 222 | else
|
---|
| 223 | {
|
---|
[29345] | 224 | /* If the service was not found, we disable this service without
|
---|
| 225 | causing VBoxService to fail. */
|
---|
[29316] | 226 | if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
|
---|
[29345] | 227 | {
|
---|
[58029] | 228 | VGSvcVerbose(0, "Guest control service is not available\n");
|
---|
[29345] | 229 | rc = VERR_SERVICE_DISABLED;
|
---|
| 230 | }
|
---|
[29202] | 231 | else
|
---|
[58029] | 232 | VGSvcError("Failed to connect to the guest control service! Error: %Rrc\n", rc);
|
---|
[27865] | 233 | }
|
---|
[75807] | 234 | RTSemEventMultiDestroy(g_hControlEvent);
|
---|
| 235 | g_hControlEvent = NIL_RTSEMEVENTMULTI;
|
---|
| 236 | g_idControlSvcClient = 0;
|
---|
[27865] | 237 | return rc;
|
---|
| 238 | }
|
---|
| 239 |
|
---|
[84209] | 240 | static int vgsvcGstCtrlInvalidate(void)
|
---|
| 241 | {
|
---|
| 242 | VGSvcVerbose(1, "Invalidating configuration ...\n");
|
---|
[27926] | 243 |
|
---|
[84209] | 244 | int rc = VINF_SUCCESS;
|
---|
| 245 |
|
---|
| 246 | g_fControlSupportsOptimizations = VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient);
|
---|
| 247 | if (g_fControlSupportsOptimizations)
|
---|
| 248 | rc = VbglR3GuestCtrlMakeMeMaster(g_idControlSvcClient);
|
---|
| 249 | if (RT_SUCCESS(rc))
|
---|
| 250 | {
|
---|
| 251 | VGSvcVerbose(3, "Guest control service client ID=%RU32%s\n",
|
---|
| 252 | g_idControlSvcClient, g_fControlSupportsOptimizations ? " w/ optimizations" : "");
|
---|
| 253 |
|
---|
| 254 | /*
|
---|
| 255 | * Report features to the host.
|
---|
| 256 | */
|
---|
| 257 | const uint64_t fGuestFeatures = VBOX_GUESTCTRL_GF_0_SET_SIZE
|
---|
[84243] | 258 | | VBOX_GUESTCTRL_GF_0_PROCESS_ARGV0
|
---|
[84548] | 259 | | VBOX_GUESTCTRL_GF_0_PROCESS_DYNAMIC_SIZES
|
---|
[99120] | 260 | | VBOX_GUESTCTRL_GF_0_PROCESS_CWD
|
---|
[98526] | 261 | #ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
|
---|
| 262 | | VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS
|
---|
| 263 | #endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
|
---|
[102831] | 264 | | VBOX_GUESTCTRL_GF_0_SHUTDOWN
|
---|
| 265 | | VBOX_GUESTCTRL_GF_0_MOUNT_POINTS_ENUM;
|
---|
[84209] | 266 | rc = VbglR3GuestCtrlReportFeatures(g_idControlSvcClient, fGuestFeatures, &g_fControlHostFeatures0);
|
---|
| 267 | if (RT_SUCCESS(rc))
|
---|
| 268 | VGSvcVerbose(3, "Host features: %#RX64\n", g_fControlHostFeatures0);
|
---|
| 269 | else
|
---|
[102831] | 270 | VGSvcVerbose(1, "Warning! Feature reporting failed: %Rrc\n", rc);
|
---|
[84209] | 271 |
|
---|
| 272 | return VINF_SUCCESS;
|
---|
| 273 | }
|
---|
| 274 | VGSvcError("Failed to become guest control master: %Rrc\n", rc);
|
---|
| 275 | VbglR3GuestCtrlDisconnect(g_idControlSvcClient);
|
---|
| 276 |
|
---|
| 277 | return rc;
|
---|
| 278 | }
|
---|
| 279 |
|
---|
[58029] | 280 | /**
|
---|
| 281 | * @interface_method_impl{VBOXSERVICE,pfnWorker}
|
---|
| 282 | */
|
---|
| 283 | static DECLCALLBACK(int) vgsvcGstCtrlWorker(bool volatile *pfShutdown)
|
---|
[27865] | 284 | {
|
---|
| 285 | /*
|
---|
[75807] | 286 | * Tell the control thread that it can continue spawning services.
|
---|
[27865] | 287 | */
|
---|
| 288 | RTThreadUserSignal(RTThreadSelf());
|
---|
[75803] | 289 | Assert(g_idControlSvcClient > 0);
|
---|
[27865] | 290 |
|
---|
[76958] | 291 | /* Allocate a scratch buffer for messages which also send
|
---|
[42846] | 292 | * payload data with them. */
|
---|
| 293 | uint32_t cbScratchBuf = _64K; /** @todo Make buffer size configurable via guest properties/argv! */
|
---|
| 294 | AssertReturn(RT_IS_POWER_OF_TWO(cbScratchBuf), VERR_INVALID_PARAMETER);
|
---|
| 295 | uint8_t *pvScratchBuf = (uint8_t*)RTMemAlloc(cbScratchBuf);
|
---|
[54944] | 296 | AssertReturn(pvScratchBuf, VERR_NO_MEMORY);
|
---|
[42846] | 297 |
|
---|
[75803] | 298 | int rc = VINF_SUCCESS; /* (shut up compiler warnings) */
|
---|
| 299 | int cRetrievalFailed = 0; /* Number of failed message retrievals in a row. */
|
---|
| 300 | while (!*pfShutdown)
|
---|
[27865] | 301 | {
|
---|
[75722] | 302 | VGSvcVerbose(3, "GstCtrl: Waiting for host msg ...\n");
|
---|
[75853] | 303 | VBGLR3GUESTCTRLCMDCTX ctxHost = { g_idControlSvcClient, 0 /*idContext*/, 2 /*uProtocol*/, 0 /*cParms*/ };
|
---|
| 304 | uint32_t idMsg = 0;
|
---|
| 305 | rc = VbglR3GuestCtrlMsgPeekWait(g_idControlSvcClient, &idMsg, &ctxHost.uNumParms, &g_idControlSession);
|
---|
[27865] | 306 | if (RT_SUCCESS(rc))
|
---|
| 307 | {
|
---|
[50810] | 308 | cRetrievalFailed = 0; /* Reset failed retrieval count. */
|
---|
[75853] | 309 | VGSvcVerbose(4, "idMsg=%RU32 (%s) (%RU32 parms) retrieved\n",
|
---|
[76958] | 310 | idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), ctxHost.uNumParms);
|
---|
[47817] | 311 |
|
---|
[75803] | 312 | /*
|
---|
| 313 | * Handle the host message.
|
---|
| 314 | */
|
---|
| 315 | switch (idMsg)
|
---|
[27865] | 316 | {
|
---|
[76958] | 317 | case HOST_MSG_CANCEL_PENDING_WAITS:
|
---|
[58029] | 318 | VGSvcVerbose(1, "We were asked to quit ...\n");
|
---|
[29438] | 319 | break;
|
---|
| 320 |
|
---|
[76958] | 321 | case HOST_MSG_SESSION_CREATE:
|
---|
[58029] | 322 | rc = vgsvcGstCtrlHandleSessionOpen(&ctxHost);
|
---|
[27865] | 323 | break;
|
---|
| 324 |
|
---|
[75807] | 325 | /* This message is also sent to the child session process (by the host). */
|
---|
[76958] | 326 | case HOST_MSG_SESSION_CLOSE:
|
---|
[58029] | 327 | rc = vgsvcGstCtrlHandleSessionClose(&ctxHost);
|
---|
[28557] | 328 | break;
|
---|
| 329 |
|
---|
[27865] | 330 | default:
|
---|
[75807] | 331 | if (VbglR3GuestCtrlSupportsOptimizations(g_idControlSvcClient))
|
---|
| 332 | {
|
---|
[75824] | 333 | rc = VbglR3GuestCtrlMsgSkip(g_idControlSvcClient, VERR_NOT_SUPPORTED, idMsg);
|
---|
[75807] | 334 | VGSvcVerbose(1, "Skipped unexpected message idMsg=%RU32 (%s), cParms=%RU32 (rc=%Rrc)\n",
|
---|
[76958] | 335 | idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), ctxHost.uNumParms, rc);
|
---|
[75807] | 336 | }
|
---|
[45010] | 337 | else
|
---|
| 338 | {
|
---|
[75803] | 339 | rc = VbglR3GuestCtrlMsgSkipOld(g_idControlSvcClient);
|
---|
[75853] | 340 | VGSvcVerbose(3, "Skipped idMsg=%RU32, cParms=%RU32, rc=%Rrc\n", idMsg, ctxHost.uNumParms, rc);
|
---|
[45010] | 341 | }
|
---|
[27865] | 342 | break;
|
---|
| 343 | }
|
---|
[75803] | 344 |
|
---|
| 345 | /* Do we need to shutdown? */
|
---|
[76958] | 346 | if (idMsg == HOST_MSG_CANCEL_PENDING_WAITS)
|
---|
[75803] | 347 | break;
|
---|
| 348 |
|
---|
| 349 | /* Let's sleep for a bit and let others run ... */
|
---|
| 350 | RTThreadYield();
|
---|
[27865] | 351 | }
|
---|
[75853] | 352 | /*
|
---|
| 353 | * Handle restore notification from host. All the context IDs (sessions,
|
---|
| 354 | * files, proceses, etc) are invalidated by a VM restore and must be closed.
|
---|
| 355 | */
|
---|
| 356 | else if (rc == VERR_VM_RESTORED)
|
---|
| 357 | {
|
---|
[95505] | 358 | VGSvcVerbose(1, "The VM session ID changed (i.e. restored), closing stale root session\n");
|
---|
| 359 |
|
---|
| 360 | /* Make sure that all other session threads are gone.
|
---|
| 361 | * This is necessary, as the new VM session (NOT to be confused with guest session!) will re-use
|
---|
| 362 | * the guest session IDs. */
|
---|
| 363 | int rc2 = VGSvcGstCtrlSessionThreadDestroyAll(&g_lstControlSessionThreads, 0 /* Flags */);
|
---|
| 364 | if (RT_FAILURE(rc2))
|
---|
| 365 | VGSvcError("Closing session threads failed with rc=%Rrc\n", rc2);
|
---|
| 366 |
|
---|
| 367 | /* Make sure to also close the root session (session 0). */
|
---|
| 368 | rc2 = VGSvcGstCtrlSessionClose(&g_Session);
|
---|
[75853] | 369 | AssertRC(rc2);
|
---|
[84209] | 370 |
|
---|
| 371 | rc2 = VbglR3GuestCtrlSessionHasChanged(g_idControlSvcClient, g_idControlSession);
|
---|
| 372 | AssertRC(rc2);
|
---|
| 373 |
|
---|
| 374 | /* Invalidate the internal state to match the current host we got restored from. */
|
---|
| 375 | rc2 = vgsvcGstCtrlInvalidate();
|
---|
| 376 | AssertRC(rc2);
|
---|
[75853] | 377 | }
|
---|
[75803] | 378 | else
|
---|
| 379 | {
|
---|
| 380 | /* Note: VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */
|
---|
| 381 | /** @todo r=bird: Above comment makes no sense. How can you get a timeout in a blocking HGCM call? */
|
---|
| 382 | VGSvcError("GstCtrl: Getting host message failed with %Rrc\n", rc);
|
---|
[27976] | 383 |
|
---|
[75803] | 384 | /* Check for VM session change. */
|
---|
[75853] | 385 | /** @todo We don't need to check the host here. */
|
---|
[75803] | 386 | uint64_t idNewSession = g_idControlSession;
|
---|
| 387 | int rc2 = VbglR3GetSessionId(&idNewSession);
|
---|
| 388 | if ( RT_SUCCESS(rc2)
|
---|
| 389 | && (idNewSession != g_idControlSession))
|
---|
| 390 | {
|
---|
| 391 | VGSvcVerbose(1, "GstCtrl: The VM session ID changed\n");
|
---|
| 392 | g_idControlSession = idNewSession;
|
---|
| 393 |
|
---|
| 394 | /* Close all opened guest sessions -- all context IDs, sessions etc.
|
---|
| 395 | * are now invalid. */
|
---|
| 396 | rc2 = VGSvcGstCtrlSessionClose(&g_Session);
|
---|
| 397 | AssertRC(rc2);
|
---|
| 398 |
|
---|
| 399 | /* Do a reconnect. */
|
---|
| 400 | VGSvcVerbose(1, "Reconnecting to HGCM service ...\n");
|
---|
| 401 | rc2 = VbglR3GuestCtrlConnect(&g_idControlSvcClient);
|
---|
| 402 | if (RT_SUCCESS(rc2))
|
---|
| 403 | {
|
---|
| 404 | VGSvcVerbose(3, "Guest control service client ID=%RU32\n", g_idControlSvcClient);
|
---|
| 405 | cRetrievalFailed = 0;
|
---|
| 406 | continue; /* Skip waiting. */
|
---|
| 407 | }
|
---|
| 408 | VGSvcError("Unable to re-connect to HGCM service, rc=%Rrc, bailing out\n", rc);
|
---|
| 409 | break;
|
---|
| 410 | }
|
---|
| 411 |
|
---|
| 412 | if (rc == VERR_INTERRUPTED)
|
---|
| 413 | RTThreadYield(); /* To be on the safe side... */
|
---|
| 414 | else if (++cRetrievalFailed <= 16) /** @todo Make this configurable? */
|
---|
| 415 | RTThreadSleep(1000); /* Wait a bit before retrying. */
|
---|
| 416 | else
|
---|
| 417 | {
|
---|
| 418 | VGSvcError("Too many failed attempts in a row to get next message, bailing out\n");
|
---|
| 419 | break;
|
---|
| 420 | }
|
---|
[28598] | 421 | }
|
---|
[27865] | 422 | }
|
---|
| 423 |
|
---|
[58029] | 424 | VGSvcVerbose(0, "Guest control service stopped\n");
|
---|
[44863] | 425 |
|
---|
[42846] | 426 | /* Delete scratch buffer. */
|
---|
| 427 | if (pvScratchBuf)
|
---|
| 428 | RTMemFree(pvScratchBuf);
|
---|
| 429 |
|
---|
[58029] | 430 | VGSvcVerbose(0, "Guest control worker returned with rc=%Rrc\n", rc);
|
---|
[27865] | 431 | return rc;
|
---|
| 432 | }
|
---|
| 433 |
|
---|
| 434 |
|
---|
[58029] | 435 | static int vgsvcGstCtrlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
|
---|
[44863] | 436 | {
|
---|
| 437 | AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
|
---|
| 438 |
|
---|
[75807] | 439 | /*
|
---|
| 440 | * Retrieve the message parameters.
|
---|
| 441 | */
|
---|
[84215] | 442 | PVBGLR3GUESTCTRLSESSIONSTARTUPINFO pStartupInfo;
|
---|
| 443 | int rc = VbglR3GuestCtrlSessionGetOpen(pHostCtx, &pStartupInfo);
|
---|
[44863] | 444 | if (RT_SUCCESS(rc))
|
---|
| 445 | {
|
---|
[75807] | 446 | /*
|
---|
| 447 | * Flat out refuse to work with protocol v1 hosts.
|
---|
| 448 | */
|
---|
[84215] | 449 | if (pStartupInfo->uProtocol == 2)
|
---|
[75807] | 450 | {
|
---|
[84215] | 451 | pHostCtx->uProtocol = pStartupInfo->uProtocol;
|
---|
[75807] | 452 | VGSvcVerbose(3, "Client ID=%RU32 now is using protocol %RU32\n", pHostCtx->uClientID, pHostCtx->uProtocol);
|
---|
[44863] | 453 |
|
---|
[75807] | 454 | /** @todo Someone explain why this code isn't in this file too? v1 support? */
|
---|
[84215] | 455 | rc = VGSvcGstCtrlSessionThreadCreate(&g_lstControlSessionThreads, pStartupInfo, NULL /* ppSessionThread */);
|
---|
[75807] | 456 | /* Report failures to the host (successes are taken care of by the session thread). */
|
---|
| 457 | }
|
---|
| 458 | else
|
---|
| 459 | {
|
---|
[84215] | 460 | VGSvcError("The host wants to use protocol v%u, we only support v2!\n", pStartupInfo->uProtocol);
|
---|
[75807] | 461 | rc = VERR_VERSION_MISMATCH;
|
---|
| 462 | }
|
---|
| 463 | if (RT_FAILURE(rc))
|
---|
| 464 | {
|
---|
| 465 | int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx, GUEST_SESSION_NOTIFYTYPE_ERROR, rc);
|
---|
| 466 | if (RT_FAILURE(rc2))
|
---|
| 467 | VGSvcError("Reporting session error status on open failed with rc=%Rrc\n", rc2);
|
---|
| 468 | }
|
---|
[44863] | 469 | }
|
---|
[75807] | 470 | else
|
---|
[44863] | 471 | {
|
---|
[75824] | 472 | VGSvcError("Error fetching parameters for opening guest session: %Rrc\n", rc);
|
---|
| 473 | VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
|
---|
[44863] | 474 | }
|
---|
[84147] | 475 |
|
---|
[84215] | 476 | VbglR3GuestCtrlSessionStartupInfoFree(pStartupInfo);
|
---|
| 477 | pStartupInfo = NULL;
|
---|
[84147] | 478 |
|
---|
[58029] | 479 | VGSvcVerbose(3, "Opening a new guest session returned rc=%Rrc\n", rc);
|
---|
[44863] | 480 | return rc;
|
---|
| 481 | }
|
---|
| 482 |
|
---|
| 483 |
|
---|
[58029] | 484 | static int vgsvcGstCtrlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx)
|
---|
[44863] | 485 | {
|
---|
| 486 | AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER);
|
---|
| 487 |
|
---|
[75824] | 488 | uint32_t idSession;
|
---|
[58029] | 489 | uint32_t fFlags;
|
---|
[75824] | 490 | int rc = VbglR3GuestCtrlSessionGetClose(pHostCtx, &fFlags, &idSession);
|
---|
[44863] | 491 | if (RT_SUCCESS(rc))
|
---|
| 492 | {
|
---|
| 493 | rc = VERR_NOT_FOUND;
|
---|
| 494 |
|
---|
[45010] | 495 | PVBOXSERVICECTRLSESSIONTHREAD pThread;
|
---|
| 496 | RTListForEach(&g_lstControlSessionThreads, pThread, VBOXSERVICECTRLSESSIONTHREAD, Node)
|
---|
[44863] | 497 | {
|
---|
[84147] | 498 | if ( pThread->pStartupInfo
|
---|
| 499 | && pThread->pStartupInfo->uSessionID == idSession)
|
---|
[44863] | 500 | {
|
---|
[58029] | 501 | rc = VGSvcGstCtrlSessionThreadDestroy(pThread, fFlags);
|
---|
[44863] | 502 | break;
|
---|
| 503 | }
|
---|
| 504 | }
|
---|
[75824] | 505 |
|
---|
| 506 | #if 0 /** @todo A bit of a mess here as this message goes to both to this process (master) and the session process. */
|
---|
[45010] | 507 | if (RT_FAILURE(rc))
|
---|
| 508 | {
|
---|
| 509 | /* Report back on failure. On success this will be done
|
---|
| 510 | * by the forked session thread. */
|
---|
[45109] | 511 | int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx,
|
---|
[45010] | 512 | GUEST_SESSION_NOTIFYTYPE_ERROR, rc);
|
---|
| 513 | if (RT_FAILURE(rc2))
|
---|
| 514 | {
|
---|
[58029] | 515 | VGSvcError("Reporting session error status on close failed with rc=%Rrc\n", rc2);
|
---|
[45010] | 516 | if (RT_SUCCESS(rc))
|
---|
| 517 | rc = rc2;
|
---|
| 518 | }
|
---|
| 519 | }
|
---|
[45415] | 520 | #endif
|
---|
[75824] | 521 | VGSvcVerbose(2, "Closing guest session %RU32 returned rc=%Rrc\n", idSession, rc);
|
---|
[44863] | 522 | }
|
---|
[45415] | 523 | else
|
---|
[75824] | 524 | {
|
---|
| 525 | VGSvcError("Error fetching parameters for closing guest session: %Rrc\n", rc);
|
---|
| 526 | VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID, rc, UINT32_MAX);
|
---|
| 527 | }
|
---|
[44863] | 528 | return rc;
|
---|
| 529 | }
|
---|
| 530 |
|
---|
| 531 |
|
---|
[58029] | 532 | /**
|
---|
| 533 | * @interface_method_impl{VBOXSERVICE,pfnStop}
|
---|
| 534 | */
|
---|
| 535 | static DECLCALLBACK(void) vgsvcGstCtrlStop(void)
|
---|
[27865] | 536 | {
|
---|
[58029] | 537 | VGSvcVerbose(3, "Stopping ...\n");
|
---|
[29438] | 538 |
|
---|
[39279] | 539 | /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
|
---|
[27865] | 540 | * annoying call since doesn't support timeouts in the posix world. */
|
---|
[39300] | 541 | if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
|
---|
| 542 | RTSemEventMultiSignal(g_hControlEvent);
|
---|
[29438] | 543 |
|
---|
| 544 | /*
|
---|
[49349] | 545 | * Ask the host service to cancel all pending requests for the main
|
---|
| 546 | * control thread so that we can shutdown properly here.
|
---|
[29438] | 547 | */
|
---|
[75803] | 548 | if (g_idControlSvcClient)
|
---|
[29438] | 549 | {
|
---|
[58029] | 550 | VGSvcVerbose(3, "Cancelling pending waits (client ID=%u) ...\n",
|
---|
[75803] | 551 | g_idControlSvcClient);
|
---|
[39300] | 552 |
|
---|
[75803] | 553 | int rc = VbglR3GuestCtrlCancelPendingWaits(g_idControlSvcClient);
|
---|
[29438] | 554 | if (RT_FAILURE(rc))
|
---|
[58029] | 555 | VGSvcError("Cancelling pending waits failed; rc=%Rrc\n", rc);
|
---|
[29438] | 556 | }
|
---|
[27865] | 557 | }
|
---|
| 558 |
|
---|
[38587] | 559 |
|
---|
[39418] | 560 | /**
|
---|
[44863] | 561 | * Destroys all guest process threads which are still active.
|
---|
| 562 | */
|
---|
[58029] | 563 | static void vgsvcGstCtrlShutdown(void)
|
---|
[44863] | 564 | {
|
---|
[58029] | 565 | VGSvcVerbose(2, "Shutting down ...\n");
|
---|
[42846] | 566 |
|
---|
[58029] | 567 | int rc2 = VGSvcGstCtrlSessionThreadDestroyAll(&g_lstControlSessionThreads, 0 /* Flags */);
|
---|
[45010] | 568 | if (RT_FAILURE(rc2))
|
---|
[58029] | 569 | VGSvcError("Closing session threads failed with rc=%Rrc\n", rc2);
|
---|
[42846] | 570 |
|
---|
[58029] | 571 | rc2 = VGSvcGstCtrlSessionClose(&g_Session);
|
---|
[45010] | 572 | if (RT_FAILURE(rc2))
|
---|
[58029] | 573 | VGSvcError("Closing session failed with rc=%Rrc\n", rc2);
|
---|
[45010] | 574 |
|
---|
[58029] | 575 | VGSvcVerbose(2, "Shutting down complete\n");
|
---|
[38157] | 576 | }
|
---|
| 577 |
|
---|
| 578 |
|
---|
[58029] | 579 | /**
|
---|
| 580 | * @interface_method_impl{VBOXSERVICE,pfnTerm}
|
---|
| 581 | */
|
---|
| 582 | static DECLCALLBACK(void) vgsvcGstCtrlTerm(void)
|
---|
[27865] | 583 | {
|
---|
[58029] | 584 | VGSvcVerbose(3, "Terminating ...\n");
|
---|
[29438] | 585 |
|
---|
[58029] | 586 | vgsvcGstCtrlShutdown();
|
---|
[28420] | 587 |
|
---|
[75803] | 588 | VGSvcVerbose(3, "Disconnecting client ID=%u ...\n", g_idControlSvcClient);
|
---|
| 589 | VbglR3GuestCtrlDisconnect(g_idControlSvcClient);
|
---|
| 590 | g_idControlSvcClient = 0;
|
---|
[27865] | 591 |
|
---|
| 592 | if (g_hControlEvent != NIL_RTSEMEVENTMULTI)
|
---|
| 593 | {
|
---|
| 594 | RTSemEventMultiDestroy(g_hControlEvent);
|
---|
| 595 | g_hControlEvent = NIL_RTSEMEVENTMULTI;
|
---|
| 596 | }
|
---|
| 597 | }
|
---|
| 598 |
|
---|
| 599 |
|
---|
[39300] | 600 | /**
|
---|
[27865] | 601 | * The 'vminfo' service description.
|
---|
| 602 | */
|
---|
| 603 | VBOXSERVICE g_Control =
|
---|
| 604 | {
|
---|
| 605 | /* pszName. */
|
---|
| 606 | "control",
|
---|
| 607 | /* pszDescription. */
|
---|
| 608 | "Host-driven Guest Control",
|
---|
| 609 | /* pszUsage. */
|
---|
[40059] | 610 | #ifdef DEBUG
|
---|
[83974] | 611 | " [--control-dump-stderr] [--control-dump-stdout]\n"
|
---|
[40059] | 612 | #endif
|
---|
[83974] | 613 | " [--control-interval <ms>]"
|
---|
[27865] | 614 | ,
|
---|
| 615 | /* pszOptions. */
|
---|
[40060] | 616 | #ifdef DEBUG
|
---|
[40059] | 617 | " --control-dump-stderr Dumps all guest proccesses stderr data to the\n"
|
---|
| 618 | " temporary directory.\n"
|
---|
| 619 | " --control-dump-stdout Dumps all guest proccesses stdout data to the\n"
|
---|
| 620 | " temporary directory.\n"
|
---|
[40060] | 621 | #endif
|
---|
[29594] | 622 | " --control-interval Specifies the interval at which to check for\n"
|
---|
[76958] | 623 | " new control messages. The default is 1000 ms.\n"
|
---|
[27865] | 624 | ,
|
---|
| 625 | /* methods */
|
---|
[58029] | 626 | vgsvcGstCtrlPreInit,
|
---|
| 627 | vgsvcGstCtrlOption,
|
---|
| 628 | vgsvcGstCtrlInit,
|
---|
| 629 | vgsvcGstCtrlWorker,
|
---|
| 630 | vgsvcGstCtrlStop,
|
---|
| 631 | vgsvcGstCtrlTerm
|
---|
[27865] | 632 | };
|
---|
| 633 |
|
---|