VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp@ 42800

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

Guest Control 2.0: Bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.4 KB
Line 
1
2/* $Id: GuestSessionImpl.cpp 42787 2012-08-13 09:49:28Z vboxsync $ */
3/** @file
4 * VirtualBox Main - XXX.
5 */
6
7/*
8 * Copyright (C) 2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include "GuestImpl.h"
24#include "GuestSessionImpl.h"
25#include "GuestCtrlImplPrivate.h"
26
27#include "Global.h"
28#include "AutoCaller.h"
29#include "ProgressImpl.h"
30
31#include <memory> /* For auto_ptr. */
32
33#include <iprt/env.h>
34#include <iprt/file.h> /* For CopyTo/From. */
35
36#include <VBox/com/array.h>
37#include <VBox/version.h>
38
39
40#define VBOX_SERVICE_ENVARG_BUG
41
42// constructor / destructor
43/////////////////////////////////////////////////////////////////////////////
44
45DEFINE_EMPTY_CTOR_DTOR(GuestSession)
46
47HRESULT GuestSession::FinalConstruct(void)
48{
49 LogFlowThisFunc(("\n"));
50 return BaseFinalConstruct();
51}
52
53void GuestSession::FinalRelease(void)
54{
55 LogFlowThisFuncEnter();
56 uninit();
57 BaseFinalRelease();
58 LogFlowThisFuncLeave();
59}
60
61// session task classes
62/////////////////////////////////////////////////////////////////////////////
63
64GuestSessionTask::GuestSessionTask(GuestSession *pSession)
65{
66 mSession = pSession;
67}
68
69GuestSessionTask::~GuestSessionTask(void)
70{
71}
72
73int GuestSessionTask::setProgress(ULONG uPercent)
74{
75 if (mProgress.isNull()) /* Progress is optional. */
76 return VINF_SUCCESS;
77
78 BOOL fCanceled;
79 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
80 && fCanceled)
81 return VERR_CANCELLED;
82 BOOL fCompleted;
83 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
84 && !fCompleted)
85 return VINF_SUCCESS;
86 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
87 if (FAILED(hr))
88 return VERR_COM_UNEXPECTED;
89
90 return VINF_SUCCESS;
91}
92
93int GuestSessionTask::setProgressSuccess(void)
94{
95 if (mProgress.isNull()) /* Progress is optional. */
96 return VINF_SUCCESS;
97
98 BOOL fCanceled;
99 BOOL fCompleted;
100 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
101 && !fCanceled
102 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
103 && !fCompleted)
104 {
105 HRESULT hr = mProgress->notifyComplete(S_OK);
106 if (FAILED(hr))
107 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
108 }
109
110 return VINF_SUCCESS;
111}
112
113HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
114{
115 if (mProgress.isNull()) /* Progress is optional. */
116 return hr; /* Return original rc. */
117
118 BOOL fCanceled;
119 BOOL fCompleted;
120 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
121 && !fCanceled
122 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
123 && !fCompleted)
124 {
125 HRESULT hr2 = mProgress->notifyComplete(hr,
126 COM_IIDOF(IGuestSession),
127 GuestSession::getStaticComponentName(),
128 strMsg.c_str());
129 if (FAILED(hr2))
130 return hr2;
131 }
132 return hr; /* Return original rc. */
133}
134
135SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
136 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
137 : mSource(strSource),
138 mDest(strDest),
139 mSourceFile(NULL),
140 mSourceOffset(0),
141 mSourceSize(0),
142 GuestSessionTask(pSession)
143{
144 mCopyFileFlags = uFlags;
145}
146
147/** @todo Merge this and the above call and let the above call do the open/close file handling so that the
148 * inner code only has to deal with file handles. No time now ... */
149SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
150 PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
151 const Utf8Str &strDest, uint32_t uFlags)
152 : GuestSessionTask(pSession)
153{
154 mSourceFile = pSourceFile;
155 mSourceOffset = cbSourceOffset;
156 mSourceSize = cbSourceSize;
157 mDest = strDest;
158 mCopyFileFlags = uFlags;
159}
160
161SessionTaskCopyTo::~SessionTaskCopyTo(void)
162{
163
164}
165
166int SessionTaskCopyTo::Run(void)
167{
168 LogFlowThisFuncEnter();
169
170 ComObjPtr<GuestSession> pSession = mSession;
171 Assert(!pSession.isNull());
172
173 AutoCaller autoCaller(pSession);
174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
175
176 if (mCopyFileFlags)
177 {
178 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
179 Utf8StrFmt(GuestSession::tr("Copy flags (%#x) not implemented yet"),
180 mCopyFileFlags));
181 return VERR_INVALID_PARAMETER;
182 }
183
184 int rc;
185
186 RTFILE fileLocal;
187 PRTFILE pFile = &fileLocal;
188
189 if (!mSourceFile)
190 {
191 /* Does our source file exist? */
192 if (!RTFileExists(mSource.c_str()))
193 {
194 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
195 Utf8StrFmt(GuestSession::tr("Source file \"%s\" does not exist or is not a file"),
196 mSource.c_str()));
197 }
198 else
199 {
200 rc = RTFileOpen(pFile, mSource.c_str(),
201 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
202 if (RT_FAILURE(rc))
203 {
204 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
205 Utf8StrFmt(GuestSession::tr("Could not open source file \"%s\" for reading: %Rrc"),
206 mSource.c_str(), rc));
207 }
208 else
209 {
210 rc = RTFileGetSize(*pFile, &mSourceSize);
211 if (RT_FAILURE(rc))
212 {
213 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
214 Utf8StrFmt(GuestSession::tr("Could not query file size of \"%s\": %Rrc"),
215 mSource.c_str(), rc));
216 }
217 }
218 }
219 }
220 else
221 {
222 pFile = mSourceFile;
223 /* Size + offset are optional. */
224 }
225
226 GuestProcessStartupInfo procInfo;
227 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to the guest to \"%s\" (%RU64 bytes)"),
228 mSource.c_str(), mDest.c_str(), mSourceSize);
229 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
230 procInfo.mFlags = ProcessCreateFlag_Hidden;
231
232 /* Set arguments.*/
233 procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", mDest.c_str())); /** @todo Do we need path conversion? */
234
235 /* Startup process. */
236 ComObjPtr<GuestProcess> pProcess;
237 rc = pSession->processCreateExInteral(procInfo, pProcess);
238 if (RT_SUCCESS(rc))
239 rc = pProcess->startProcess();
240 if (RT_FAILURE(rc))
241 {
242 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
243 Utf8StrFmt(GuestSession::tr("Unable to start guest process: %Rrc"), rc));
244 }
245 else
246 {
247 GuestProcessWaitResult waitRes;
248 BYTE byBuf[_64K];
249
250 BOOL fCanceled = FALSE;
251 uint64_t cbWrittenTotal = 0;
252 uint64_t cbToRead = mSourceSize;
253
254 for (;;)
255 {
256 rc = pProcess->waitFor(ProcessWaitForFlag_StdIn,
257 30 * 1000 /* Timeout */, waitRes);
258 if ( RT_FAILURE(rc)
259 || ( waitRes.mResult != ProcessWaitResult_StdIn
260 && waitRes.mResult != ProcessWaitResult_Any))
261 {
262 break;
263 }
264
265 size_t cbRead = 0;
266 if (mSourceSize) /* If we have nothing to write, take a shortcut. */
267 {
268 /** @todo Not very efficient, but works for now. */
269 rc = RTFileSeek(*pFile, mSourceOffset + cbWrittenTotal,
270 RTFILE_SEEK_BEGIN, NULL /* poffActual */);
271 if (RT_SUCCESS(rc))
272 {
273 rc = RTFileRead(*pFile, (uint8_t*)byBuf,
274 RT_MIN(cbToRead, sizeof(byBuf)), &cbRead);
275 /*
276 * Some other error occured? There might be a chance that RTFileRead
277 * could not resolve/map the native error code to an IPRT code, so just
278 * print a generic error.
279 */
280 if (RT_FAILURE(rc))
281 {
282 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
283 Utf8StrFmt(GuestSession::tr("Could not read from file \"%s\" (%Rrc)"),
284 mSource.c_str(), rc));
285 break;
286 }
287 }
288 else
289 {
290 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
291 Utf8StrFmt(GuestSession::tr("Seeking file \"%s\" offset %RU64 failed: %Rrc"),
292 mSource.c_str(), cbWrittenTotal, rc));
293 break;
294 }
295 }
296
297 uint32_t fFlags = ProcessInputFlag_None;
298
299 /* Did we reach the end of the content we want to transfer (last chunk)? */
300 if ( (cbRead < sizeof(byBuf))
301 /* Did we reach the last block which is exactly _64K? */
302 || (cbToRead - cbRead == 0)
303 /* ... or does the user want to cancel? */
304 || ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
305 && fCanceled)
306 )
307 {
308 fFlags |= ProcessInputFlag_EndOfFile;
309 }
310
311 uint32_t cbWritten;
312 Assert(sizeof(byBuf) >= cbRead);
313 rc = pProcess->writeData(0 /* StdIn */, fFlags,
314 byBuf, cbRead,
315 30 * 1000 /* Timeout */, &cbWritten);
316 if (RT_FAILURE(rc))
317 {
318 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
319 Utf8StrFmt(GuestSession::tr("Writing to file \"%s\" (offset %RU64) failed: %Rrc"),
320 mDest.c_str(), cbWrittenTotal, rc));
321 break;
322 }
323
324 LogFlowThisFunc(("cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
325 cbWritten, cbToRead - cbWritten, cbWrittenTotal + cbWritten, mSourceSize));
326
327 /* Only subtract bytes reported written by the guest. */
328 Assert(cbToRead >= cbWritten);
329 cbToRead -= cbWritten;
330
331 /* Update total bytes written to the guest. */
332 cbWrittenTotal += cbWritten;
333 Assert(cbWrittenTotal <= mSourceSize);
334
335 /* Did the user cancel the operation above? */
336 if (fCanceled)
337 break;
338
339 /* Update the progress.
340 * Watch out for division by zero. */
341 mSourceSize > 0
342 ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / mSourceSize))
343 : rc = setProgress(100);
344 if (RT_FAILURE(rc))
345 break;
346
347 /* End of file reached? */
348 if (!cbToRead)
349 break;
350 } /* for */
351
352 if ( !fCanceled
353 || RT_SUCCESS(rc))
354 {
355 /*
356 * Even if we succeeded until here make sure to check whether we really transfered
357 * everything.
358 */
359 if ( mSourceSize > 0
360 && cbWrittenTotal == 0)
361 {
362 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
363 * to the destination -> access denied. */
364 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
365 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
366 mSource.c_str(), mDest.c_str()));
367 }
368 else if (cbWrittenTotal < mSourceSize)
369 {
370 /* If we did not copy all let the user know. */
371 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
372 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RU64 bytes transfered)"),
373 mSource.c_str(), cbWrittenTotal, mSourceSize));
374 }
375 else
376 {
377 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
378 30 * 1000 /* Timeout */, waitRes);
379 if ( RT_FAILURE(rc)
380 || waitRes.mResult != ProcessWaitResult_Terminate)
381 {
382 if (RT_FAILURE(rc))
383 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
384 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed: %Rrc"),
385 mSource.c_str(), rc));
386 else
387 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
388 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed with wait result %ld"),
389 mSource.c_str(), waitRes.mResult));
390 }
391
392 if (RT_SUCCESS(rc))
393 {
394 ProcessStatus_T procStatus;
395 LONG exitCode;
396 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
397 && procStatus != ProcessStatus_TerminatedNormally)
398 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
399 && exitCode != 0)
400 )
401 {
402 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
403 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %ld"),
404 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
405 }
406 }
407
408 if (RT_SUCCESS(rc))
409 rc = setProgressSuccess();
410 }
411 }
412
413 pProcess->close();
414 } /* processCreateExInteral */
415
416 if (!mSourceFile) /* Only close locally opened files. */
417 RTFileClose(*pFile);
418
419 LogFlowFuncLeaveRC(rc);
420 return rc;
421}
422
423int SessionTaskCopyTo::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
424{
425 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, mCopyFileFlags=%x\n",
426 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mCopyFileFlags));
427
428 mDesc = strDesc;
429 mProgress = pProgress;
430
431 int rc = RTThreadCreate(NULL, SessionTaskCopyTo::taskThread, this,
432 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
433 "gctlCpyTo");
434 LogFlowFuncLeaveRC(rc);
435 return rc;
436}
437
438/* static */
439int SessionTaskCopyTo::taskThread(RTTHREAD Thread, void *pvUser)
440{
441 std::auto_ptr<SessionTaskCopyTo> task(static_cast<SessionTaskCopyTo*>(pvUser));
442 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
443
444 LogFlowFunc(("pTask=%p\n", task.get()));
445 return task->Run();
446}
447
448SessionTaskCopyFrom::SessionTaskCopyFrom(GuestSession *pSession,
449 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
450 : GuestSessionTask(pSession)
451{
452 mSource = strSource;
453 mDest = strDest;
454 mFlags = uFlags;
455}
456
457SessionTaskCopyFrom::~SessionTaskCopyFrom(void)
458{
459
460}
461
462int SessionTaskCopyFrom::Run(void)
463{
464 LogFlowThisFuncEnter();
465
466 ComObjPtr<GuestSession> pSession = mSession;
467 Assert(!pSession.isNull());
468
469 AutoCaller autoCaller(pSession);
470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
471
472 /*
473 * Note: There will be races between querying file size + reading the guest file's
474 * content because we currently *do not* lock down the guest file when doing the
475 * actual operations.
476 ** @todo Implement guest file locking!
477 */
478 GuestFsObjData objData;
479 int rc = pSession->fileQueryInfoInternal(Utf8Str(mSource), objData);
480 if (RT_FAILURE(rc))
481 {
482 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
483 Utf8StrFmt(GuestSession::tr("Querying guest file information for \"%s\" failed: %Rrc"),
484 mSource.c_str(), rc));
485 }
486 else if (objData.mType != FsObjType_File) /* Only single files are supported at the moment. */
487 {
488 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
489 Utf8StrFmt(GuestSession::tr("Object \"%s\" on the guest is not a file"), mSource.c_str()));
490 }
491
492 if (RT_SUCCESS(rc))
493 {
494 RTFILE fileDest;
495 rc = RTFileOpen(&fileDest, mDest.c_str(),
496 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
497 if (RT_FAILURE(rc))
498 {
499 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
500 Utf8StrFmt(GuestSession::tr("Error opening destination file \"%s\": %Rrc"),
501 mDest.c_str(), rc));
502 }
503 else
504 {
505 GuestProcessStartupInfo procInfo;
506 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
507 mSource.c_str(), mDest.c_str(), objData.mObjectSize);
508 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
509 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
510
511 /* Set arguments.*/
512 procInfo.mArguments.push_back(mSource); /* Which file to output? */
513
514 /* Startup process. */
515 ComObjPtr<GuestProcess> pProcess;
516 rc = pSession->processCreateExInteral(procInfo, pProcess);
517 if (RT_SUCCESS(rc))
518 rc = pProcess->startProcess();
519 if (RT_FAILURE(rc))
520 {
521 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
522 Utf8StrFmt(GuestSession::tr("Unable to start guest process for copying data from guest to host: %Rrc"), rc));
523 }
524 else
525 {
526 GuestProcessWaitResult waitRes;
527 BYTE byBuf[_64K];
528
529 BOOL fCanceled = FALSE;
530 uint64_t cbWrittenTotal = 0;
531 uint64_t cbToRead = objData.mObjectSize;
532
533 for (;;)
534 {
535 rc = pProcess->waitFor(ProcessWaitForFlag_StdOut,
536 30 * 1000 /* Timeout */, waitRes);
537 if ( waitRes.mResult == ProcessWaitResult_StdOut
538 || waitRes.mResult == ProcessWaitResult_Any)
539 {
540 size_t cbRead;
541 rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
542 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
543 &cbRead);
544 if (RT_FAILURE(rc))
545 {
546 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
547 Utf8StrFmt(GuestSession::tr("Reading from file \"%s\" (offset %RU64) failed: %Rrc"),
548 mSource.c_str(), cbWrittenTotal, rc));
549 break;
550 }
551
552 if (cbRead)
553 {
554 rc = RTFileWrite(fileDest, byBuf, cbRead, NULL /* No partial writes */);
555 if (RT_FAILURE(rc))
556 {
557 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
558 Utf8StrFmt(GuestSession::tr("Error writing to file \"%s\" (%RU64 bytes left): %Rrc"),
559 mDest.c_str(), cbToRead, rc));
560 break;
561 }
562
563 /* Only subtract bytes reported written by the guest. */
564 Assert(cbToRead >= cbRead);
565 cbToRead -= cbRead;
566
567 /* Update total bytes written to the guest. */
568 cbWrittenTotal += cbRead;
569 Assert(cbWrittenTotal <= (uint64_t)objData.mObjectSize);
570
571 /* Did the user cancel the operation above? */
572 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
573 && fCanceled)
574 break;
575
576 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)objData.mObjectSize / 100.0)));
577 if (RT_FAILURE(rc))
578 break;
579 }
580 }
581 else if ( RT_FAILURE(rc)
582 || waitRes.mResult == ProcessWaitResult_Terminate
583 || waitRes.mResult == ProcessWaitResult_Error
584 || waitRes.mResult == ProcessWaitResult_Timeout)
585 {
586 if (RT_FAILURE(waitRes.mRC))
587 rc = waitRes.mRC;
588 break;
589 }
590 } /* for */
591
592 LogFlowThisFunc(("rc=%Rrc, cbWrittenTotal=%RU64, cbSize=%RI64, cbToRead=%RU64\n",
593 rc, cbWrittenTotal, objData.mObjectSize, cbToRead));
594
595 if ( !fCanceled
596 || RT_SUCCESS(rc))
597 {
598 /*
599 * Even if we succeeded until here make sure to check whether we really transfered
600 * everything.
601 */
602 if ( objData.mObjectSize > 0
603 && cbWrittenTotal == 0)
604 {
605 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
606 * to the destination -> access denied. */
607 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
608 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
609 mSource.c_str(), mDest.c_str()));
610 rc = VERR_GENERAL_FAILURE; /* Fudge. */
611 }
612 else if (cbWrittenTotal < (uint64_t)objData.mObjectSize)
613 {
614 /* If we did not copy all let the user know. */
615 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
616 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RI64 bytes transfered)"),
617 mSource.c_str(), cbWrittenTotal, objData.mObjectSize));
618 rc = VERR_GENERAL_FAILURE; /* Fudge. */
619 }
620 else
621 {
622 ProcessStatus_T procStatus;
623 LONG exitCode;
624 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
625 && procStatus != ProcessStatus_TerminatedNormally)
626 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
627 && exitCode != 0)
628 )
629 {
630 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
631 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %d"),
632 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
633 rc = VERR_GENERAL_FAILURE; /* Fudge. */
634 }
635 else /* Yay, success! */
636 rc = setProgressSuccess();
637 }
638 }
639
640 pProcess->close();
641 }
642
643 RTFileClose(fileDest);
644 }
645 }
646
647 LogFlowFuncLeaveRC(rc);
648 return rc;
649}
650
651int SessionTaskCopyFrom::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
652{
653 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, uFlags=%x\n",
654 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mFlags));
655
656 mDesc = strDesc;
657 mProgress = pProgress;
658
659 int rc = RTThreadCreate(NULL, SessionTaskCopyFrom::taskThread, this,
660 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
661 "gctlCpyFrom");
662 LogFlowFuncLeaveRC(rc);
663 return rc;
664}
665
666/* static */
667int SessionTaskCopyFrom::taskThread(RTTHREAD Thread, void *pvUser)
668{
669 std::auto_ptr<SessionTaskCopyFrom> task(static_cast<SessionTaskCopyFrom*>(pvUser));
670 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
671
672 LogFlowFunc(("pTask=%p\n", task.get()));
673 return task->Run();
674}
675
676SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
677 const Utf8Str &strSource, uint32_t uFlags)
678 : GuestSessionTask(pSession)
679{
680 mSource = strSource;
681 mFlags = uFlags;
682}
683
684SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
685{
686
687}
688
689int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
690 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
691 bool fOptional, uint32_t *pcbSize)
692{
693 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
694 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
695 /* pcbSize is optional. */
696
697 uint32_t cbOffset;
698 size_t cbSize;
699
700 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbOffset, &cbSize);
701 if (RT_FAILURE(rc))
702 {
703 if (fOptional)
704 return VINF_SUCCESS;
705
706 return rc;
707 }
708
709 Assert(cbOffset);
710 Assert(cbSize);
711 rc = RTFileSeek(pISO->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
712
713 /* Copy over the Guest Additions file to the guest. */
714 if (RT_SUCCESS(rc))
715 {
716 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
717 strFileSource.c_str(), strFileDest.c_str()));
718
719 if (RT_SUCCESS(rc))
720 {
721 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(pSession /* GuestSession */,
722 &pISO->file, cbOffset, cbSize,
723 strFileDest, CopyFileFlag_None);
724 AssertPtrReturn(pTask, VERR_NO_MEMORY);
725
726 ComObjPtr<Progress> pProgressCopyTo;
727 rc = pSession->startTaskAsync(Utf8StrFmt(GuestSession::tr("Copying Guest Additions installer file \"%s\" to \"%s\" on guest"),
728 mSource.c_str(), strFileDest.c_str()),
729 pTask, pProgressCopyTo);
730 if (RT_SUCCESS(rc))
731 {
732 BOOL fCanceled = FALSE;
733 HRESULT hr = pProgressCopyTo->WaitForCompletion(-1);
734 if ( SUCCEEDED(pProgressCopyTo->COMGETTER(Canceled)(&fCanceled))
735 && fCanceled)
736 {
737 rc = VERR_GENERAL_FAILURE; /* Fudge. */
738 }
739 else if (FAILED(hr))
740 {
741 Assert(FAILED(hr));
742 rc = VERR_GENERAL_FAILURE; /* Fudge. */
743 }
744 }
745 }
746 }
747
748 /** @todo Note: Since there is no file locking involved at the moment, there can be modifications
749 * between finished copying, the verification and the actual execution. */
750
751 /* Determine where the installer image ended up and if it has the correct size. */
752 if (RT_SUCCESS(rc))
753 {
754 LogRel(("Verifying Guest Additions installer file \"%s\" ...\n", strFileDest.c_str()));
755
756 GuestFsObjData objData;
757 int64_t cbSizeOnGuest;
758 rc = pSession->fileQuerySizeInternal(strFileDest, &cbSizeOnGuest);
759#ifdef VBOX_SERVICE_ENVARG_BUG
760 if (RT_FAILURE(rc))
761 {
762 /* Ugly hack: Because older Guest Additions have problems with environment variable
763 expansion in parameters we have to check an alternative location on Windows.
764 So check for "%TEMP%\" being "C:\\Windows\\system32\\EMP" actually. */
765 if (strFileDest.startsWith("%TEMP%\\", RTCString::CaseSensitive))
766 {
767 Utf8Str strFileDestBug = "C:\\Windows\\system32\\EMP" + strFileDest.substr(sizeof("%TEMP%\\") - sizeof(char));
768 rc = pSession->fileQuerySizeInternal(strFileDestBug, &cbSizeOnGuest);
769 }
770 }
771#endif
772 if ( RT_SUCCESS(rc)
773 && cbSize == (uint64_t)cbSizeOnGuest)
774 {
775 LogRel(("Guest Additions installer file \"%s\" successfully verified\n",
776 strFileDest.c_str()));
777 }
778 else
779 {
780 if (RT_SUCCESS(rc)) /* Size does not match. */
781 rc = VERR_BROKEN_PIPE; /** @todo FInd a better error. */
782 }
783
784 if (RT_SUCCESS(rc))
785 {
786 if (pcbSize)
787 *pcbSize = cbSizeOnGuest;
788 }
789 }
790
791 return rc;
792}
793
794int SessionTaskUpdateAdditions::runFile(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
795{
796 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
797
798#ifdef VBOX_SERVICE_ENVARG_BUG
799 GuestFsObjData objData;
800 int rc = pSession->fileQueryInfoInternal(procInfo.mCommand, objData);
801 if (RT_FAILURE(rc))
802 procInfo.mCommand = "C:\\Windows\\system32\\EMP" + procInfo.mCommand.substr(sizeof("%TEMP%\\") - sizeof(char));
803#endif
804
805 ComObjPtr<GuestProcess> pProcess;
806 rc = pSession->processCreateExInteral(procInfo, pProcess);
807 if (RT_SUCCESS(rc))
808 rc = pProcess->startProcess();
809
810 if (RT_SUCCESS(rc))
811 {
812 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
813
814 GuestProcessWaitResult waitRes;
815 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
816 10 * 60 * 1000 /* 10 mins Timeout */, waitRes);
817 if (waitRes.mResult == ProcessWaitResult_Terminate)
818 {
819 ProcessStatus_T procStatus;
820 LONG exitCode;
821 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
822 && procStatus != ProcessStatus_TerminatedNormally)
823 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
824 && exitCode != 0)
825 )
826 {
827 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
828 Utf8StrFmt(GuestSession::tr("Running %s failed with status %ld, exit code %ld"),
829 procInfo.mName.c_str(), procStatus, exitCode));
830 rc = VERR_GENERAL_FAILURE; /* Fudge. */
831 }
832 else /* Yay, success! */
833 {
834 LogRel(("%s successfully completed\n", procInfo.mName.c_str()));
835 }
836 }
837 else
838 {
839 if (RT_FAILURE(rc))
840 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
841 Utf8StrFmt(GuestSession::tr("Error while waiting running %s: %Rrc"),
842 procInfo.mName.c_str(), rc));
843 else
844 {
845 setProgressErrorMsg(VBOX_E_IPRT_ERROR, pProcess->errorMsg());
846 rc = VERR_GENERAL_FAILURE; /* Fudge. */
847 }
848 }
849 }
850
851 if (!pProcess.isNull())
852 pProcess->close();
853
854 return rc;
855}
856
857int SessionTaskUpdateAdditions::Run(void)
858{
859 LogFlowThisFuncEnter();
860
861 ComObjPtr<GuestSession> pSession = mSession;
862 Assert(!pSession.isNull());
863
864 AutoCaller autoCaller(pSession);
865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
866
867 int rc = setProgress(10);
868 if (RT_FAILURE(rc))
869 return rc;
870
871 HRESULT hr = S_OK;
872
873 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
874
875 /*
876 * Determine guest OS type and the required installer image.
877 * At the moment only Windows guests are supported.
878 */
879 Utf8Str strInstallerImage;
880
881 ComObjPtr<Guest> pGuest(mSession->getParent());
882 Bstr osTypeId;
883 if ( SUCCEEDED(pGuest->COMGETTER(OSTypeId(osTypeId.asOutParam())))
884 && !osTypeId.isEmpty())
885 {
886 Utf8Str osTypeIdUtf8(osTypeId); /* Needed for .contains(). */
887 if ( osTypeIdUtf8.contains("Microsoft", Utf8Str::CaseInsensitive)
888 || osTypeIdUtf8.contains("Windows", Utf8Str::CaseInsensitive))
889 {
890 if (osTypeIdUtf8.contains("64", Utf8Str::CaseInsensitive))
891 strInstallerImage = "VBOXWINDOWSADDITIONS_AMD64.EXE";
892 else
893 strInstallerImage = "VBOXWINDOWSADDITIONS_X86.EXE";
894 /* Since the installers are located in the root directory,
895 * no further path processing needs to be done (yet). */
896 }
897 else /* Everything else is not supported (yet). */
898 {
899 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
900 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
901 osTypeIdUtf8.c_str()));
902 rc = VERR_GENERAL_FAILURE; /* Fudge. */
903 }
904 }
905 else
906 {
907 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
908 Utf8StrFmt(GuestSession::tr("Could not detected guest OS type/version, please update manually")));
909 rc = VERR_GENERAL_FAILURE; /* Fudge. */
910 }
911
912 RTISOFSFILE iso;
913 if (RT_SUCCESS(rc))
914 {
915 Assert(!strInstallerImage.isEmpty());
916
917 /*
918 * Try to open the .ISO file and locate the specified installer.
919 */
920 rc = RTIsoFsOpen(&iso, mSource.c_str());
921 if (RT_FAILURE(rc))
922 {
923 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
924 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
925 mSource.c_str(), rc));
926 }
927 else
928 {
929 rc = setProgress(5);
930
931 /** @todo Add support for non-Windows as well! */
932 Utf8Str strInstallerDest = "%TEMP%\\VBoxWindowsAdditions.exe";
933 bool fInstallCertificates = false;
934
935 if (RT_SUCCESS(rc))
936 {
937 /*
938 * Copy over main installer to the guest.
939 */
940 rc = copyFileToGuest(pSession, &iso, strInstallerImage, strInstallerDest,
941 false /* File is not optional */, NULL /* cbSize */);
942 if (RT_SUCCESS(rc))
943 rc = setProgress(20);
944
945 /*
946 * Install needed certificates for the WHQL crap.
947 ** @todo Only for Windows!
948 */
949 if (RT_SUCCESS(rc))
950 {
951 rc = copyFileToGuest(pSession, &iso, "CERT/ORACLE_VBOX.CER", "%TEMP%\\oracle-vbox.cer",
952 true /* File is optional */, NULL /* cbSize */);
953 if (RT_SUCCESS(rc))
954 {
955 rc = setProgress(30);
956 if (RT_SUCCESS(rc))
957 {
958 rc = copyFileToGuest(pSession, &iso, "CERT/VBOXCERTUTIL.EXE", "%TEMP%\\VBoxCertUtil.exe",
959 true /* File is optional */, NULL /* cbSize */);
960 if (RT_SUCCESS(rc))
961 {
962 fInstallCertificates = true;
963 rc = setProgress(40);
964 }
965 else
966 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
967 Utf8StrFmt(GuestSession::tr("Error while copying certificate installation tool to the guest: %Rrc"), rc));
968 }
969 }
970 else
971 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
972 Utf8StrFmt(GuestSession::tr("Error while copying certificate to the guest: %Rrc"), rc));
973 }
974 }
975
976 /*
977 * Run VBoxCertUtil.exe to install the Oracle certificate.
978 */
979 if ( RT_SUCCESS(rc)
980 && fInstallCertificates)
981 {
982 LogRel(("Installing certificates on the guest ...\n"));
983
984 GuestProcessStartupInfo procInfo;
985 procInfo.mName = Utf8StrFmt(GuestSession::tr("VirtualBox Guest Additions Certificate Utility"));
986 procInfo.mCommand = Utf8Str("%TEMP%\\VBoxCertUtil.exe");
987 procInfo.mFlags = ProcessCreateFlag_Hidden;
988
989 /* Construct arguments. */
990 /** @todo Remove hardcoded paths. */
991 procInfo.mArguments.push_back(Utf8Str("add-trusted-publisher"));
992 /* Ugly hack: Because older Guest Additions have problems with environment variable
993 expansion in parameters we have to check an alternative location on Windows.
994 So check for "%TEMP%\VBoxWindowsAdditions.exe" in a screwed up way. */
995#ifdef VBOX_SERVICE_ENVARG_BUG
996 GuestFsObjData objData;
997 rc = pSession->fileQueryInfoInternal("%TEMP%\\oracle-vbox.cer", objData);
998 if (RT_SUCCESS(rc))
999#endif
1000 procInfo.mArguments.push_back(Utf8Str("%TEMP%\\oracle-vbox.cer"));
1001#ifdef VBOX_SERVICE_ENVARG_BUG
1002 else
1003 procInfo.mArguments.push_back(Utf8Str("C:\\Windows\\system32\\EMPoracle-vbox.cer"));
1004#endif
1005 /* Overwrite rc in any case. */
1006 rc = runFile(pSession, procInfo);
1007 }
1008
1009 if (RT_SUCCESS(rc))
1010 rc = setProgress(60);
1011
1012 if (RT_SUCCESS(rc))
1013 {
1014 LogRel(("Updating Guest Additions ...\n"));
1015
1016 GuestProcessStartupInfo procInfo;
1017 procInfo.mName = Utf8StrFmt(GuestSession::tr("VirtualBox Guest Additions Setup"));
1018 procInfo.mCommand = Utf8Str(strInstallerDest);
1019 procInfo.mFlags = ProcessCreateFlag_Hidden;
1020 /* If the caller does not want to wait for out guest update process to end,
1021 * complete the progress object now so that the caller can do other work. */
1022 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1023 procInfo.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1024
1025 /* Construct arguments. */
1026 procInfo.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1027 procInfo.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1028 /* Don't quit VBoxService during upgrade because it still is used for this
1029 * piece of code we're in right now (that is, here!) ... */
1030 procInfo.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1031 /* Tell the installer to report its current installation status
1032 * using a running VBoxTray instance via balloon messages in the
1033 * Windows taskbar. */
1034 procInfo.mArguments.push_back(Utf8Str("/post_installstatus"));
1035
1036 rc = runFile(pSession, procInfo);
1037 if (RT_SUCCESS(rc))
1038 hr = setProgressSuccess();
1039 }
1040 RTIsoFsClose(&iso);
1041 }
1042 }
1043
1044 LogFlowFuncLeaveRC(rc);
1045 return rc;
1046}
1047
1048int SessionTaskUpdateAdditions::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
1049{
1050 LogFlowThisFunc(("strDesc=%s, strSource=%s, uFlags=%x\n",
1051 strDesc.c_str(), mSource.c_str(), mFlags));
1052
1053 mDesc = strDesc;
1054 mProgress = pProgress;
1055
1056 int rc = RTThreadCreate(NULL, SessionTaskUpdateAdditions::taskThread, this,
1057 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1058 "gctlUpGA");
1059 LogFlowFuncLeaveRC(rc);
1060 return rc;
1061}
1062
1063/* static */
1064int SessionTaskUpdateAdditions::taskThread(RTTHREAD Thread, void *pvUser)
1065{
1066 std::auto_ptr<SessionTaskUpdateAdditions> task(static_cast<SessionTaskUpdateAdditions*>(pvUser));
1067 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1068
1069 LogFlowFunc(("pTask=%p\n", task.get()));
1070 return task->Run();
1071}
1072
1073// public initializer/uninitializer for internal purposes only
1074/////////////////////////////////////////////////////////////////////////////
1075
1076int GuestSession::init(Guest *aGuest, ULONG aSessionID,
1077 Utf8Str aUser, Utf8Str aPassword, Utf8Str aDomain, Utf8Str aName)
1078{
1079 LogFlowThisFuncEnter();
1080
1081 AssertPtrReturn(aGuest, VERR_INVALID_POINTER);
1082
1083 /* Enclose the state transition NotReady->InInit->Ready. */
1084 AutoInitSpan autoInitSpan(this);
1085 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
1086
1087 mData.mTimeout = 30 * 60 * 1000; /* Session timeout is 30 mins by default. */
1088 mData.mParent = aGuest;
1089 mData.mId = aSessionID;
1090
1091 mData.mCredentials.mUser = aUser;
1092 mData.mCredentials.mPassword = aPassword;
1093 mData.mCredentials.mDomain = aDomain;
1094 mData.mName = aName;
1095
1096 /* Confirm a successful initialization when it's the case. */
1097 autoInitSpan.setSucceeded();
1098
1099 LogFlowFuncLeaveRC(VINF_SUCCESS);
1100 return VINF_SUCCESS;
1101}
1102
1103/**
1104 * Uninitializes the instance.
1105 * Called from FinalRelease().
1106 */
1107void GuestSession::uninit(void)
1108{
1109 LogFlowThisFuncEnter();
1110
1111 /* Enclose the state transition Ready->InUninit->NotReady. */
1112 AutoUninitSpan autoUninitSpan(this);
1113 if (autoUninitSpan.uninitDone())
1114 return;
1115
1116#ifdef VBOX_WITH_GUEST_CONTROL
1117 for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
1118 itDirs != mData.mDirectories.end(); ++itDirs)
1119 {
1120 (*itDirs)->uninit();
1121 (*itDirs).setNull();
1122 }
1123 mData.mDirectories.clear();
1124
1125 for (SessionFiles::iterator itFiles = mData.mFiles.begin();
1126 itFiles != mData.mFiles.end(); ++itFiles)
1127 {
1128 (*itFiles)->uninit();
1129 (*itFiles).setNull();
1130 }
1131 mData.mFiles.clear();
1132
1133 for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
1134 itProcs != mData.mProcesses.end(); ++itProcs)
1135 {
1136 itProcs->second->close();
1137 }
1138
1139 for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
1140 itProcs != mData.mProcesses.end(); ++itProcs)
1141 {
1142 itProcs->second->uninit();
1143 itProcs->second.setNull();
1144 }
1145 mData.mProcesses.clear();
1146
1147 mData.mParent->sessionClose(this);
1148
1149 LogFlowThisFuncLeave();
1150#endif
1151}
1152
1153// implementation of public getters/setters for attributes
1154/////////////////////////////////////////////////////////////////////////////
1155
1156STDMETHODIMP GuestSession::COMGETTER(User)(BSTR *aUser)
1157{
1158#ifndef VBOX_WITH_GUEST_CONTROL
1159 ReturnComNotImplemented();
1160#else
1161 LogFlowThisFuncEnter();
1162
1163 CheckComArgOutPointerValid(aUser);
1164
1165 AutoCaller autoCaller(this);
1166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1167
1168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 mData.mCredentials.mUser.cloneTo(aUser);
1171
1172 LogFlowFuncLeaveRC(S_OK);
1173 return S_OK;
1174#endif /* VBOX_WITH_GUEST_CONTROL */
1175}
1176
1177STDMETHODIMP GuestSession::COMGETTER(Domain)(BSTR *aDomain)
1178{
1179#ifndef VBOX_WITH_GUEST_CONTROL
1180 ReturnComNotImplemented();
1181#else
1182 LogFlowThisFuncEnter();
1183
1184 CheckComArgOutPointerValid(aDomain);
1185
1186 AutoCaller autoCaller(this);
1187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1188
1189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 mData.mCredentials.mDomain.cloneTo(aDomain);
1192
1193 LogFlowFuncLeaveRC(S_OK);
1194 return S_OK;
1195#endif /* VBOX_WITH_GUEST_CONTROL */
1196}
1197
1198STDMETHODIMP GuestSession::COMGETTER(Name)(BSTR *aName)
1199{
1200#ifndef VBOX_WITH_GUEST_CONTROL
1201 ReturnComNotImplemented();
1202#else
1203 LogFlowThisFuncEnter();
1204
1205 CheckComArgOutPointerValid(aName);
1206
1207 AutoCaller autoCaller(this);
1208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1209
1210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1211
1212 mData.mName.cloneTo(aName);
1213
1214 LogFlowFuncLeaveRC(S_OK);
1215 return S_OK;
1216#endif /* VBOX_WITH_GUEST_CONTROL */
1217}
1218
1219STDMETHODIMP GuestSession::COMGETTER(Id)(ULONG *aId)
1220{
1221#ifndef VBOX_WITH_GUEST_CONTROL
1222 ReturnComNotImplemented();
1223#else
1224 LogFlowThisFuncEnter();
1225
1226 CheckComArgOutPointerValid(aId);
1227
1228 AutoCaller autoCaller(this);
1229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1230
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 *aId = mData.mId;
1234
1235 LogFlowFuncLeaveRC(S_OK);
1236 return S_OK;
1237#endif /* VBOX_WITH_GUEST_CONTROL */
1238}
1239
1240STDMETHODIMP GuestSession::COMGETTER(Timeout)(ULONG *aTimeout)
1241{
1242#ifndef VBOX_WITH_GUEST_CONTROL
1243 ReturnComNotImplemented();
1244#else
1245 LogFlowThisFuncEnter();
1246
1247 CheckComArgOutPointerValid(aTimeout);
1248
1249 AutoCaller autoCaller(this);
1250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1251
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 *aTimeout = mData.mTimeout;
1255
1256 LogFlowFuncLeaveRC(S_OK);
1257 return S_OK;
1258#endif /* VBOX_WITH_GUEST_CONTROL */
1259}
1260
1261STDMETHODIMP GuestSession::COMSETTER(Timeout)(ULONG aTimeout)
1262{
1263#ifndef VBOX_WITH_GUEST_CONTROL
1264 ReturnComNotImplemented();
1265#else
1266 LogFlowThisFuncEnter();
1267
1268 AutoCaller autoCaller(this);
1269 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1270
1271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1272
1273 mData.mTimeout = aTimeout;
1274
1275 LogFlowFuncLeaveRC(S_OK);
1276 return S_OK;
1277#endif /* VBOX_WITH_GUEST_CONTROL */
1278}
1279
1280STDMETHODIMP GuestSession::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnvironment))
1281{
1282#ifndef VBOX_WITH_GUEST_CONTROL
1283 ReturnComNotImplemented();
1284#else
1285 LogFlowThisFuncEnter();
1286
1287 CheckComArgOutSafeArrayPointerValid(aEnvironment);
1288
1289 AutoCaller autoCaller(this);
1290 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1291
1292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1293
1294 size_t cEnvVars = mData.mEnvironment.Size();
1295 LogFlowThisFunc(("%s cEnvVars=%RU32\n", mData.mName.c_str(), cEnvVars));
1296 com::SafeArray<BSTR> environment(cEnvVars);
1297
1298 for (size_t i = 0; i < cEnvVars; i++)
1299 {
1300 Bstr strEnv(mData.mEnvironment.Get(i));
1301 strEnv.cloneTo(&environment[i]);
1302 }
1303 environment.detachTo(ComSafeArrayOutArg(aEnvironment));
1304
1305 LogFlowFuncLeaveRC(S_OK);
1306 return S_OK;
1307#endif /* VBOX_WITH_GUEST_CONTROL */
1308}
1309
1310STDMETHODIMP GuestSession::COMSETTER(Environment)(ComSafeArrayIn(IN_BSTR, aValues))
1311{
1312#ifndef VBOX_WITH_GUEST_CONTROL
1313 ReturnComNotImplemented();
1314#else
1315 LogFlowThisFuncEnter();
1316
1317 AutoCaller autoCaller(this);
1318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1319
1320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1321
1322 com::SafeArray<IN_BSTR> environment(ComSafeArrayInArg(aValues));
1323
1324 int rc = VINF_SUCCESS;
1325 for (size_t i = 0; i < environment.size() && RT_SUCCESS(rc); i++)
1326 {
1327 Utf8Str strEnv(environment[i]);
1328 if (!strEnv.isEmpty()) /* Silently skip empty entries. */
1329 rc = mData.mEnvironment.Set(strEnv);
1330 }
1331
1332 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1333 LogFlowFuncLeaveRC(hr);
1334 return hr;
1335#endif /* VBOX_WITH_GUEST_CONTROL */
1336}
1337
1338STDMETHODIMP GuestSession::COMGETTER(Processes)(ComSafeArrayOut(IGuestProcess *, aProcesses))
1339{
1340#ifndef VBOX_WITH_GUEST_CONTROL
1341 ReturnComNotImplemented();
1342#else
1343 LogFlowThisFuncEnter();
1344
1345 CheckComArgOutSafeArrayPointerValid(aProcesses);
1346
1347 AutoCaller autoCaller(this);
1348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1349
1350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 SafeIfaceArray<IGuestProcess> collection(mData.mProcesses);
1353 collection.detachTo(ComSafeArrayOutArg(aProcesses));
1354
1355 LogFlowFuncLeaveRC(S_OK);
1356 return S_OK;
1357#endif /* VBOX_WITH_GUEST_CONTROL */
1358}
1359
1360STDMETHODIMP GuestSession::COMGETTER(Directories)(ComSafeArrayOut(IGuestDirectory *, aDirectories))
1361{
1362#ifndef VBOX_WITH_GUEST_CONTROL
1363 ReturnComNotImplemented();
1364#else
1365 LogFlowThisFuncEnter();
1366
1367 CheckComArgOutSafeArrayPointerValid(aDirectories);
1368
1369 AutoCaller autoCaller(this);
1370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1371
1372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1373
1374 SafeIfaceArray<IGuestDirectory> collection(mData.mDirectories);
1375 collection.detachTo(ComSafeArrayOutArg(aDirectories));
1376
1377 LogFlowFuncLeaveRC(S_OK);
1378 return S_OK;
1379#endif /* VBOX_WITH_GUEST_CONTROL */
1380}
1381
1382STDMETHODIMP GuestSession::COMGETTER(Files)(ComSafeArrayOut(IGuestFile *, aFiles))
1383{
1384#ifndef VBOX_WITH_GUEST_CONTROL
1385 ReturnComNotImplemented();
1386#else
1387 LogFlowThisFuncEnter();
1388
1389 CheckComArgOutSafeArrayPointerValid(aFiles);
1390
1391 AutoCaller autoCaller(this);
1392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1393
1394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 SafeIfaceArray<IGuestFile> collection(mData.mFiles);
1397 collection.detachTo(ComSafeArrayOutArg(aFiles));
1398
1399 LogFlowFuncLeaveRC(S_OK);
1400 return S_OK;
1401#endif /* VBOX_WITH_GUEST_CONTROL */
1402}
1403
1404// private methods
1405/////////////////////////////////////////////////////////////////////////////
1406
1407int GuestSession::directoryClose(ComObjPtr<GuestDirectory> pDirectory)
1408{
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
1412 itDirs != mData.mDirectories.end(); ++itDirs)
1413 {
1414 if (pDirectory == (*itDirs))
1415 {
1416 mData.mDirectories.erase(itDirs);
1417 return VINF_SUCCESS;
1418 }
1419 }
1420
1421 return VERR_NOT_FOUND;
1422}
1423
1424int GuestSession::directoryCreateInternal(const Utf8Str &strPath, uint32_t uMode, uint32_t uFlags, ComObjPtr<GuestDirectory> &pDirectory)
1425{
1426 LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n",
1427 strPath.c_str(), uMode, uFlags));
1428
1429 GuestProcessStartupInfo procInfo;
1430 procInfo.mName = Utf8StrFmt(tr("Creating directory \"%s\"", strPath.c_str()));
1431 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_MKDIR);
1432 procInfo.mFlags = ProcessCreateFlag_Hidden;
1433
1434 int rc = VINF_SUCCESS;
1435
1436 /* Construct arguments. */
1437 if (uFlags & DirectoryCreateFlag_Parents)
1438 procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
1439 if (uMode)
1440 {
1441 procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */
1442
1443 char szMode[16];
1444 if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode))
1445 {
1446 procInfo.mArguments.push_back(Utf8Str(szMode));
1447 }
1448 else
1449 rc = VERR_INVALID_PARAMETER;
1450 }
1451 procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
1452
1453 ComObjPtr<GuestProcess> pProcess;
1454 rc = processCreateExInteral(procInfo, pProcess);
1455 if (RT_SUCCESS(rc))
1456 rc = pProcess->startProcess();
1457 if (RT_SUCCESS(rc))
1458 {
1459 GuestProcessWaitResult waitRes;
1460 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate, 30 * 1000 /* Timeout */, waitRes);
1461 if (RT_SUCCESS(rc))
1462 {
1463 ProcessStatus_T procStatus;
1464 HRESULT hr = pProcess->COMGETTER(Status)(&procStatus);
1465 ComAssertComRC(hr);
1466 if (procStatus == ProcessStatus_TerminatedNormally)
1467 {
1468 LONG lExitCode;
1469 pProcess->COMGETTER(ExitCode)(&lExitCode);
1470 if (lExitCode != 0)
1471 return VERR_CANT_CREATE;
1472 }
1473 else
1474 rc = VERR_BROKEN_PIPE; /** @todo Find a better rc. */
1475 }
1476 }
1477
1478 if (RT_FAILURE(rc))
1479 return rc;
1480
1481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1482
1483 /* Create the directory object. */
1484 HRESULT hr = pDirectory.createObject();
1485 if (FAILED(hr))
1486 return VERR_COM_UNEXPECTED;
1487
1488 /* Note: There will be a race between creating and getting/initing the directory
1489 object here. */
1490 rc = pDirectory->init(this /* Parent */, strPath);
1491 if (RT_FAILURE(rc))
1492 return rc;
1493
1494 /* Add the created directory to our vector. */
1495 mData.mDirectories.push_back(pDirectory);
1496
1497 LogFlowFunc(("Added new directory \"%s\" (Session: %RU32)\n",
1498 strPath.c_str(), mData.mId));
1499
1500 LogFlowFuncLeaveRC(rc);
1501 return rc;
1502}
1503
1504int GuestSession::objectCreateTempInternal(Utf8Str strTemplate,
1505 Utf8Str strPath,
1506 bool fDirectory,
1507 Utf8Str &strName, int *prc)
1508{
1509 GuestProcessStartupInfo procInfo;
1510 GuestProcessStream streamOut;
1511 ComObjPtr<GuestProcess> pProcess;
1512 int rc = VINF_SUCCESS;
1513
1514 if (fDirectory)
1515 procInfo.mName = Utf8StrFmt(tr("Creating temporary directory from template \"%s\"",
1516 strTemplate.c_str()));
1517 else
1518 procInfo.mName = Utf8StrFmt(tr("Creating temporary file from template \"%s\"",
1519 strTemplate.c_str()));
1520 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_MKTEMP);
1521 procInfo.mFlags = ProcessCreateFlag_Hidden
1522 | ProcessCreateFlag_WaitForStdOut;
1523 /* Construct arguments. */
1524 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1525 if (fDirectory)
1526 procInfo.mArguments.push_back(Utf8Str("-d"));
1527 if (strPath.length()) /* Otherwise use /tmp or equivalent. */
1528 {
1529 procInfo.mArguments.push_back(Utf8Str("-t"));
1530 procInfo.mArguments.push_back(strPath);
1531 }
1532 procInfo.mArguments.push_back(strTemplate);
1533
1534 rc = processCreateExInteral(procInfo, pProcess);
1535 if (RT_SUCCESS(rc))
1536 {
1537 GuestProcessWaitResult waitRes;
1538 BYTE byBuf[_64K];
1539 size_t cbRead;
1540
1541 for (;;)
1542 {
1543 rc = pProcess->waitFor(ProcessWaitForFlag_StdOut,
1544 30 * 1000 /* Timeout */, waitRes);
1545 if ( RT_FAILURE(rc)
1546 || waitRes.mResult == ProcessWaitResult_Terminate
1547 || waitRes.mResult == ProcessWaitResult_Error
1548 || waitRes.mResult == ProcessWaitResult_Timeout)
1549 {
1550 break;
1551 }
1552
1553 rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
1554 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
1555 &cbRead);
1556 if (RT_FAILURE(rc))
1557 break;
1558
1559 if (cbRead)
1560 {
1561 rc = streamOut.AddData(byBuf, cbRead);
1562 if (RT_FAILURE(rc))
1563 break;
1564 }
1565 }
1566
1567 LogFlowThisFunc(("rc=%Rrc, cbRead=%RU32, cbStreamOut=%RU32\n",
1568 rc, cbRead, streamOut.GetSize()));
1569 }
1570 else
1571 LogThisFunc(("Error while starting temporary object creation tool on guest: %Rrc\n", rc));
1572 if (RT_FAILURE(rc))
1573 LogThisFunc(("Error while running temporary object creation tool: %Rrc\n", rc));
1574 else if (!streamOut.GetSize())
1575 {
1576 LogThisFunc(("No return code after creating temporary object\n"));
1577 rc = VERR_NO_DATA;
1578 }
1579 if (RT_SUCCESS(rc))
1580 {
1581 const char *pcszName;
1582 int64_t i64rc;
1583 GuestProcessStreamBlock streamBlock;
1584 rc = streamOut.ParseBlock(streamBlock);
1585 if (RT_SUCCESS(rc))
1586 {
1587 pcszName = streamBlock.GetString("name");
1588 if (pcszName)
1589 strName = pcszName;
1590 else
1591 {
1592 LogThisFunc(("No name returned after creating temporary object\n"));
1593 rc = VERR_NO_DATA;
1594 }
1595 if (RT_FAILURE(rc = streamBlock.GetInt64Ex("rc", &i64rc)))
1596 LogThisFunc(("No return code after creating temporary object\n"));
1597 }
1598 if ( RT_SUCCESS(rc)
1599 && ( i64rc == VERR_INVALID_PARAMETER
1600 || i64rc == VERR_NOT_SUPPORTED))
1601 rc = (int)i64rc;
1602 if (RT_SUCCESS(rc))
1603 *prc = (int)i64rc;
1604 }
1605 else
1606 LogThisFunc(("Error while getting return code from creating temporary object: %Rrc\n", rc));
1607 return rc;
1608}
1609
1610int GuestSession::directoryOpenInternal(const Utf8Str &strPath, const Utf8Str &strFilter,
1611 uint32_t uFlags, ComObjPtr<GuestDirectory> &pDirectory)
1612{
1613 LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n",
1614 strPath.c_str(), strFilter.c_str(), uFlags));
1615
1616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 /* Create the directory object. */
1619 HRESULT hr = pDirectory.createObject();
1620 if (FAILED(hr))
1621 return VERR_COM_UNEXPECTED;
1622
1623 int rc = pDirectory->init(this /* Parent */,
1624 strPath, strFilter, uFlags);
1625 if (RT_FAILURE(rc))
1626 return rc;
1627
1628 /* Add the created directory to our vector. */
1629 mData.mDirectories.push_back(pDirectory);
1630
1631 LogFlowFunc(("Added new directory \"%s\" (Session: %RU32)\n",
1632 strPath.c_str(), mData.mId));
1633
1634 LogFlowFuncLeaveRC(rc);
1635 return rc;
1636}
1637
1638int GuestSession::dispatchToProcess(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
1639{
1640 LogFlowFuncEnter();
1641
1642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1643
1644 uint32_t uProcessID = VBOX_GUESTCTRL_CONTEXTID_GET_PROCESS(uContextID);
1645#ifdef DEBUG
1646 LogFlowFunc(("uProcessID=%RU32 (%RU32 total)\n",
1647 uProcessID, mData.mProcesses.size()));
1648#endif
1649 int rc;
1650 SessionProcesses::const_iterator itProc
1651 = mData.mProcesses.find(uProcessID);
1652 if (itProc != mData.mProcesses.end())
1653 {
1654 ComObjPtr<GuestProcess> pProcess(itProc->second);
1655 Assert(!pProcess.isNull());
1656
1657 alock.release();
1658 rc = pProcess->callbackDispatcher(uContextID, uFunction, pvData, cbData);
1659 }
1660 else
1661 rc = VERR_NOT_FOUND;
1662
1663 LogFlowFuncLeaveRC(rc);
1664 return rc;
1665}
1666
1667int GuestSession::fileClose(ComObjPtr<GuestFile> pFile)
1668{
1669 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 for (SessionFiles::iterator itFiles = mData.mFiles.begin();
1672 itFiles != mData.mFiles.end(); ++itFiles)
1673 {
1674 if (pFile == (*itFiles))
1675 {
1676 mData.mFiles.erase(itFiles);
1677 return VINF_SUCCESS;
1678 }
1679 }
1680
1681 return VERR_NOT_FOUND;
1682}
1683
1684/**
1685 * Implementation of FileRemove(). Can throw an exception due to the use of
1686 * Utf8Str, Utf8StrFmt and std::vector near the beginning (and others?). The
1687 * caller should catch this. On success, *prc will be set to the return code
1688 * of the delete operation to distinguish between API and command failure.
1689 */
1690int GuestSession::fileRemoveInternal(Utf8Str strPath, int *prc)
1691{
1692 GuestProcessStartupInfo procInfo;
1693 GuestProcessStream streamOut;
1694 int rc = VINF_SUCCESS;
1695
1696 AssertPtrReturn(prc, VERR_INVALID_POINTER);
1697 procInfo.mName = Utf8StrFmt(tr("Removing file \"%s\"",
1698 strPath.c_str()));
1699 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_RM);
1700 procInfo.mFlags = ProcessCreateFlag_Hidden
1701 | ProcessCreateFlag_WaitForStdOut;
1702 /* Construct arguments. */
1703 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1704 procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
1705
1706 ComObjPtr<GuestProcess> pProcess;
1707 rc = processCreateExInteral(procInfo, pProcess);
1708 if (RT_SUCCESS(rc))
1709 rc = pProcess->startProcess();
1710 if (RT_SUCCESS(rc))
1711 {
1712 GuestProcessWaitResult waitRes;
1713 BYTE byBuf[_64K];
1714 size_t cbRead;
1715
1716 for (;;)
1717 {
1718 rc = pProcess->waitFor(ProcessWaitForFlag_StdOut,
1719 30 * 1000 /* Timeout */, waitRes);
1720 if ( RT_FAILURE(rc)
1721 || waitRes.mResult == ProcessWaitResult_Terminate
1722 || waitRes.mResult == ProcessWaitResult_Error
1723 || waitRes.mResult == ProcessWaitResult_Timeout)
1724 {
1725 break;
1726 }
1727
1728 rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
1729 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
1730 &cbRead);
1731 if (RT_FAILURE(rc))
1732 break;
1733
1734 if (cbRead)
1735 {
1736 rc = streamOut.AddData(byBuf, cbRead);
1737 if (RT_FAILURE(rc))
1738 break;
1739 }
1740 }
1741
1742 LogFlowThisFunc(("rc=%Rrc, cbRead=%RU32, cbStreamOut=%RU32\n",
1743 rc, cbRead, streamOut.GetSize()));
1744 }
1745 else
1746 LogThisFunc(("Error starting delete tool on guest: %Rrc\n", rc));
1747 if (RT_FAILURE(rc))
1748 LogThisFunc(("Error running delete tool on guest: %Rrc\n", rc));
1749 else if (!streamOut.GetSize())
1750 {
1751 LogThisFunc(("No return code after deleting file"));
1752 rc = VERR_NO_DATA;
1753 }
1754 if (RT_SUCCESS(rc))
1755 {
1756 GuestProcessStreamBlock streamBlock;
1757 int64_t i64rc;
1758 rc = streamOut.ParseBlock(streamBlock);
1759 streamBlock.GetString("fname");
1760 rc = streamBlock.GetInt64Ex("rc", &i64rc);
1761 if (RT_SUCCESS(rc))
1762 *prc = (int)i64rc;
1763 }
1764 else
1765 Log(("Error getting return code from deleting file: %Rrc\n", rc));
1766 return rc;
1767}
1768
1769int GuestSession::fileOpenInternal(const Utf8Str &strPath, const Utf8Str &strOpenMode, const Utf8Str &strDisposition,
1770 uint32_t uCreationMode, int64_t iOffset, ComObjPtr<GuestFile> &pFile)
1771{
1772 LogFlowThisFunc(("strPath=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%x, iOffset=%RI64\n",
1773 strPath.c_str(), strOpenMode.c_str(), strDisposition.c_str(), uCreationMode, iOffset));
1774
1775 /* Create the directory object. */
1776 HRESULT hr = pFile.createObject();
1777 if (FAILED(hr))
1778 return VERR_COM_UNEXPECTED;
1779
1780 /* Note: There will be a race between creating and getting/initing the directory
1781 object here. */
1782 int rc = pFile->init(this /* Parent */,
1783 strPath, strOpenMode, strDisposition, uCreationMode, iOffset);
1784 if (RT_FAILURE(rc))
1785 return rc;
1786
1787 /* Add the created directory to our vector. */
1788 mData.mFiles.push_back(pFile);
1789
1790 LogFlowFunc(("Added new file \"%s\" (Session: %RU32\n",
1791 strPath.c_str(), mData.mId));
1792
1793 LogFlowFuncLeaveRC(rc);
1794 return rc;
1795}
1796
1797/* Note: Will work on directories and others, too. */
1798int GuestSession::fileQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData)
1799{
1800 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1801
1802 GuestProcessStartupInfo procInfo;
1803 procInfo.mName = Utf8StrFmt(tr("Querying info for \"%s\""), strPath.c_str());
1804 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_STAT);
1805 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
1806
1807 /* Construct arguments. */
1808 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1809 procInfo.mArguments.push_back(strPath);
1810
1811 GuestProcessStream streamOut;
1812
1813 ComObjPtr<GuestProcess> pProcess;
1814 int rc = processCreateExInteral(procInfo, pProcess);
1815 if (RT_SUCCESS(rc))
1816 rc = pProcess->startProcess();
1817 if (RT_SUCCESS(rc))
1818 {
1819 GuestProcessWaitResult waitRes;
1820 BYTE byBuf[_64K];
1821 size_t cbRead = 0;
1822
1823 /** @todo Merge with GuestDirectory::read. */
1824 for (;;)
1825 {
1826 rc = pProcess->waitFor(ProcessWaitForFlag_StdOut,
1827 30 * 1000 /* Timeout */, waitRes);
1828 if ( RT_FAILURE(rc)
1829 || waitRes.mResult == ProcessWaitResult_Terminate
1830 || waitRes.mResult == ProcessWaitResult_Error
1831 || waitRes.mResult == ProcessWaitResult_Timeout)
1832 {
1833 break;
1834 }
1835
1836 rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
1837 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
1838 &cbRead);
1839 if (RT_FAILURE(rc))
1840 break;
1841
1842 if (cbRead)
1843 {
1844 rc = streamOut.AddData(byBuf, cbRead);
1845 if (RT_FAILURE(rc))
1846 break;
1847 }
1848 }
1849
1850 LogFlowThisFunc(("rc=%Rrc, cbRead=%RU64, cbStreamOut=%RU32\n",
1851 rc, cbRead, streamOut.GetSize()));
1852 }
1853
1854 if (RT_SUCCESS(rc))
1855 {
1856 GuestProcessStreamBlock streamBlock;
1857 rc = streamOut.ParseBlock(streamBlock);
1858 if (RT_SUCCESS(rc))
1859 {
1860 rc = objData.FromStat(streamBlock);
1861 }
1862 else
1863 AssertMsgFailed(("Parsing stream block failed: %Rrc\n", rc));
1864 }
1865
1866 LogFlowFuncLeaveRC(rc);
1867 return rc;
1868}
1869
1870int GuestSession::fileQuerySizeInternal(const Utf8Str &strPath, int64_t *pllSize)
1871{
1872 AssertPtrReturn(pllSize, VERR_INVALID_POINTER);
1873
1874 GuestFsObjData objData;
1875 int rc = fileQueryInfoInternal(strPath, objData);
1876 if (RT_SUCCESS(rc))
1877 {
1878 if (objData.mType == FsObjType_File)
1879 *pllSize = objData.mObjectSize;
1880 else
1881 rc = VERR_NOT_A_FILE;
1882 }
1883
1884 return rc;
1885}
1886
1887const GuestCredentials& GuestSession::getCredentials(void)
1888{
1889 return mData.mCredentials;
1890}
1891
1892const GuestEnvironment& GuestSession::getEnvironment(void)
1893{
1894 return mData.mEnvironment;
1895}
1896
1897Utf8Str GuestSession::getName(void)
1898{
1899 return mData.mName;
1900}
1901
1902int GuestSession::processClose(ComObjPtr<GuestProcess> pProcess)
1903{
1904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
1907 itProcs != mData.mProcesses.end(); ++itProcs)
1908 {
1909 if (pProcess == itProcs->second)
1910 {
1911 LogFlowFunc(("Removing process (Session: %RU32) with process ID=%RU32, guest PID=%RU32 (now total %ld processes)\n",
1912 mData.mId, itProcs->second->getProcessID(), itProcs->second->getPID(), mData.mProcesses.size() - 1));
1913
1914 mData.mProcesses.erase(itProcs);
1915 return VINF_SUCCESS;
1916 }
1917 }
1918
1919 return VERR_NOT_FOUND;
1920}
1921
1922/**
1923 * Creates but does *not* start the process yet. See GuestProcess::startProcess() or
1924 * GuestProcess::startProcessAsync() for that.
1925 *
1926 * @return IPRT status code.
1927 * @param procInfo
1928 * @param pProcess
1929 */
1930int GuestSession::processCreateExInteral(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
1931{
1932 LogFlowFunc(("mCmd=%s, mFlags=%x, mTimeoutMS=%RU32\n",
1933 procInfo.mCommand.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
1934#ifdef DEBUG
1935 if (procInfo.mArguments.size())
1936 {
1937 LogFlowFunc(("Arguments:"));
1938 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
1939 while (it != procInfo.mArguments.end())
1940 {
1941 LogFlow((" %s", (*it).c_str()));
1942 it++;
1943 }
1944 LogFlow(("\n"));
1945 }
1946#endif
1947
1948 /* Validate flags. */
1949 if (procInfo.mFlags)
1950 {
1951 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
1952 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1953 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
1954 && !(procInfo.mFlags & ProcessCreateFlag_NoProfile)
1955 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1956 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
1957 {
1958 return VERR_INVALID_PARAMETER;
1959 }
1960 }
1961
1962 /* Adjust timeout. If set to 0, we define
1963 * an infinite timeout. */
1964 if (procInfo.mTimeoutMS == 0)
1965 procInfo.mTimeoutMS = UINT32_MAX;
1966
1967 /** @tood Implement process priority + affinity. */
1968
1969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1970
1971 int rc = VERR_MAX_PROCS_REACHED;
1972 if (mData.mProcesses.size() >= VBOX_GUESTCTRL_MAX_PROCESSES)
1973 return rc;
1974
1975 /* Create a new (host-based) process ID and assign it. */
1976 uint32_t uNewProcessID = 0;
1977 ULONG uTries = 0;
1978
1979 for (;;)
1980 {
1981 /* Is the context ID already used? */
1982 if (!processExists(uNewProcessID, NULL /* pProgress */))
1983 {
1984 /* Callback with context ID was not found. This means
1985 * we can use this context ID for our new callback we want
1986 * to add below. */
1987 rc = VINF_SUCCESS;
1988 break;
1989 }
1990 uNewProcessID++;
1991 if (uNewProcessID == VBOX_GUESTCTRL_MAX_PROCESSES)
1992 uNewProcessID = 0;
1993
1994 if (++uTries == UINT32_MAX)
1995 break; /* Don't try too hard. */
1996 }
1997
1998 if (RT_FAILURE(rc))
1999 return rc;
2000
2001 /* Create the process object. */
2002 HRESULT hr = pProcess.createObject();
2003 if (FAILED(hr))
2004 return VERR_COM_UNEXPECTED;
2005
2006 rc = pProcess->init(mData.mParent->getConsole() /* Console */, this /* Session */,
2007 uNewProcessID, procInfo);
2008 if (RT_FAILURE(rc))
2009 return rc;
2010
2011 /* Add the created process to our map. */
2012 mData.mProcesses[uNewProcessID] = pProcess;
2013
2014 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %ld processes)\n",
2015 mData.mId, uNewProcessID, mData.mProcesses.size()));
2016
2017 return rc;
2018}
2019
2020inline bool GuestSession::processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2021{
2022 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2023 if (it != mData.mProcesses.end())
2024 {
2025 if (pProcess)
2026 *pProcess = it->second;
2027 return true;
2028 }
2029 return false;
2030}
2031
2032inline int GuestSession::processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2033{
2034 AssertReturn(uPID, false);
2035 /* pProcess is optional. */
2036
2037 SessionProcesses::iterator it = mData.mProcesses.begin();
2038 for (; it != mData.mProcesses.end(); it++)
2039 {
2040 ComObjPtr<GuestProcess> pCurProc = it->second;
2041 AutoCaller procCaller(pCurProc);
2042 if (procCaller.rc())
2043 return VERR_COM_INVALID_OBJECT_STATE;
2044
2045 if (it->second->getPID() == uPID)
2046 {
2047 if (pProcess)
2048 *pProcess = pCurProc;
2049 return VINF_SUCCESS;
2050 }
2051 }
2052
2053 return VERR_NOT_FOUND;
2054}
2055
2056int GuestSession::startTaskAsync(const Utf8Str &strTaskDesc,
2057 GuestSessionTask *pTask, ComObjPtr<Progress> &pProgress)
2058{
2059 LogFlowThisFunc(("strTaskDesc=%s, pTask=%p\n", strTaskDesc.c_str(), pTask));
2060
2061 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
2062
2063 /* Create the progress object. */
2064 HRESULT hr = pProgress.createObject();
2065 if (FAILED(hr))
2066 return VERR_COM_UNEXPECTED;
2067
2068 hr = pProgress->init(static_cast<IGuestSession*>(this),
2069 Bstr(strTaskDesc).raw(),
2070 TRUE /* aCancelable */);
2071 if (FAILED(hr))
2072 return VERR_COM_UNEXPECTED;
2073
2074 /* Initialize our worker task. */
2075 std::auto_ptr<GuestSessionTask> task(pTask);
2076
2077 int rc = task->RunAsync(strTaskDesc, pProgress);
2078 if (RT_FAILURE(rc))
2079 return rc;
2080
2081 /* Don't destruct on success. */
2082 task.release();
2083
2084 LogFlowFuncLeaveRC(rc);
2085 return rc;
2086}
2087
2088/**
2089 * Queries/collects information prior to establishing a guest session.
2090 * This is necessary to know which guest control protocol version to use,
2091 * among other things (later).
2092 *
2093 * @return IPRT status code.
2094 */
2095int GuestSession::queryInfo(void)
2096{
2097#if 1
2098 /* Since the new functions were not implemented yet, force Main to use protocol ver 1. */
2099 mData.mProtocolVersion = 1;
2100#else
2101 /*
2102 * Try querying the guest control protocol version running on the guest.
2103 * This is done using the Guest Additions version
2104 */
2105 ComObjPtr<Guest> pGuest = mData.mParent;
2106 Assert(!pGuest.isNull());
2107
2108 uint32_t uVerAdditions = pGuest->getAdditionsVersion();
2109 mData.mProtocolVersion = ( VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions) >= 4
2110 && VBOX_FULL_VERSION_GET_MINOR(uVerAdditions) >= 2) /** @todo What's about v5.0 ? */
2111 ? 2 /* Guest control 2.0. */
2112 : 1; /* Legacy guest control (VBox < 4.2). */
2113 /* Build revision is ignored. */
2114
2115 /* Tell the user but don't bitch too often. */
2116 static short s_gctrlLegacyWarning = 0;
2117 if (s_gctrlLegacyWarning++ < 3) /** @todo Find a bit nicer text. */
2118 LogRel((tr("Warning: Guest Additions are older (%ld.%ld) than host capabilities for guest control, please upgrade them. Using protocol version %ld now\n"),
2119 VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions), VBOX_FULL_VERSION_GET_MINOR(uVerAdditions), mData.mProtocolVersion));
2120#endif
2121 return VINF_SUCCESS;
2122}
2123
2124// implementation of public methods
2125/////////////////////////////////////////////////////////////////////////////
2126
2127STDMETHODIMP GuestSession::Close(void)
2128{
2129#ifndef VBOX_WITH_GUEST_CONTROL
2130 ReturnComNotImplemented();
2131#else
2132 LogFlowThisFuncEnter();
2133
2134 uninit();
2135
2136 LogFlowFuncLeaveRC(S_OK);
2137 return S_OK;
2138#endif /* VBOX_WITH_GUEST_CONTROL */
2139}
2140
2141STDMETHODIMP GuestSession::CopyFrom(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(CopyFileFlag_T, aFlags), IProgress **aProgress)
2142{
2143#ifndef VBOX_WITH_GUEST_CONTROL
2144 ReturnComNotImplemented();
2145#else
2146 CheckComArgStrNotEmptyOrNull(aSource);
2147 CheckComArgStrNotEmptyOrNull(aDest);
2148 CheckComArgOutPointerValid(aProgress);
2149
2150 LogFlowThisFuncEnter();
2151
2152 if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
2153 return setError(E_INVALIDARG, tr("No source specified"));
2154 if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
2155 return setError(E_INVALIDARG, tr("No destination specified"));
2156
2157 AutoCaller autoCaller(this);
2158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2159
2160 uint32_t fFlags = CopyFileFlag_None;
2161 if (aFlags)
2162 {
2163 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
2164 for (size_t i = 0; i < flags.size(); i++)
2165 fFlags |= flags[i];
2166 }
2167
2168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 HRESULT hr = S_OK;
2171
2172 ComObjPtr<Progress> pProgress;
2173 SessionTaskCopyFrom *pTask = new SessionTaskCopyFrom(this /* GuestSession */,
2174 Utf8Str(aSource), Utf8Str(aDest), fFlags);
2175 AssertPtrReturn(pTask, VERR_NO_MEMORY);
2176 int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from guest to \"%ls\" on the host"), aSource, aDest),
2177 pTask, pProgress);
2178 if (RT_SUCCESS(rc))
2179 {
2180 /* Return progress to the caller. */
2181 hr = pProgress.queryInterfaceTo(aProgress);
2182 }
2183 else
2184 hr = setError(VBOX_E_IPRT_ERROR,
2185 tr("Starting task for copying file \"%ls\" from guest to \"%ls\" on the host failed: %Rrc"), rc);
2186 return hr;
2187#endif /* VBOX_WITH_GUEST_CONTROL */
2188}
2189
2190STDMETHODIMP GuestSession::CopyTo(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(CopyFileFlag_T, aFlags), IProgress **aProgress)
2191{
2192#ifndef VBOX_WITH_GUEST_CONTROL
2193 ReturnComNotImplemented();
2194#else
2195 CheckComArgStrNotEmptyOrNull(aSource);
2196 CheckComArgStrNotEmptyOrNull(aDest);
2197 CheckComArgOutPointerValid(aProgress);
2198
2199 LogFlowThisFuncEnter();
2200
2201 if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
2202 return setError(E_INVALIDARG, tr("No source specified"));
2203 if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
2204 return setError(E_INVALIDARG, tr("No destination specified"));
2205
2206 AutoCaller autoCaller(this);
2207 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2208
2209 uint32_t fFlags = CopyFileFlag_None;
2210 if (aFlags)
2211 {
2212 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
2213 for (size_t i = 0; i < flags.size(); i++)
2214 fFlags |= flags[i];
2215 }
2216
2217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2218
2219 HRESULT hr = S_OK;
2220
2221 ComObjPtr<Progress> pProgress;
2222 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(this /* GuestSession */,
2223 Utf8Str(aSource), Utf8Str(aDest), fFlags);
2224 AssertPtrReturn(pTask, VERR_NO_MEMORY);
2225 int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from host to \"%ls\" on the guest"), aSource, aDest),
2226 pTask, pProgress);
2227 if (RT_SUCCESS(rc))
2228 {
2229 /* Return progress to the caller. */
2230 hr = pProgress.queryInterfaceTo(aProgress);
2231 }
2232 else
2233 hr = setError(VBOX_E_IPRT_ERROR,
2234 tr("Starting task for copying file \"%ls\" from host to \"%ls\" on the guest failed: %Rrc"), rc);
2235 return hr;
2236#endif /* VBOX_WITH_GUEST_CONTROL */
2237}
2238
2239STDMETHODIMP GuestSession::DirectoryCreate(IN_BSTR aPath, ULONG aMode,
2240 ComSafeArrayIn(DirectoryCreateFlag_T, aFlags), IGuestDirectory **aDirectory)
2241{
2242#ifndef VBOX_WITH_GUEST_CONTROL
2243 ReturnComNotImplemented();
2244#else
2245 LogFlowThisFuncEnter();
2246
2247 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2248 return setError(E_INVALIDARG, tr("No directory to create specified"));
2249 /* aDirectory is optional. */
2250
2251 AutoCaller autoCaller(this);
2252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2253
2254 uint32_t fFlags = DirectoryCreateFlag_None;
2255 if (aFlags)
2256 {
2257 com::SafeArray<DirectoryCreateFlag_T> flags(ComSafeArrayInArg(aFlags));
2258 for (size_t i = 0; i < flags.size(); i++)
2259 fFlags |= flags[i];
2260
2261 if (fFlags)
2262 {
2263 if (!(fFlags & DirectoryCreateFlag_Parents))
2264 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
2265 }
2266 }
2267
2268 HRESULT hr = S_OK;
2269
2270 ComObjPtr <GuestDirectory> pDirectory;
2271 int rc = directoryCreateInternal(Utf8Str(aPath), (uint32_t)aMode, fFlags, pDirectory);
2272 if (RT_SUCCESS(rc))
2273 {
2274 if (aDirectory)
2275 {
2276 /* Return directory object to the caller. */
2277 hr = pDirectory.queryInterfaceTo(aDirectory);
2278 }
2279 else
2280 {
2281 rc = directoryClose(pDirectory);
2282 if (RT_FAILURE(rc))
2283 hr = setError(VBOX_E_IPRT_ERROR, tr("Unable to close directory object, rc=%Rrc"), rc);
2284 }
2285 }
2286 else
2287 {
2288 switch (rc)
2289 {
2290 case VERR_INVALID_PARAMETER:
2291 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Invalid parameters given"));
2292 break;
2293
2294 case VERR_BROKEN_PIPE:
2295 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted"));
2296 break;
2297
2298 case VERR_CANT_CREATE:
2299 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Could not create directory"));
2300 break;
2301
2302 default:
2303 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
2304 break;
2305 }
2306 }
2307
2308 return hr;
2309#endif /* VBOX_WITH_GUEST_CONTROL */
2310}
2311
2312STDMETHODIMP GuestSession::DirectoryCreateTemp(IN_BSTR aTemplate, ULONG aMode, IN_BSTR aPath, BOOL aSecure, BSTR *aDirectory)
2313{
2314#ifndef VBOX_WITH_GUEST_CONTROL
2315 ReturnComNotImplemented();
2316#else
2317 LogFlowThisFuncEnter();
2318
2319 if (RT_UNLIKELY((aTemplate) == NULL || *(aTemplate) == '\0'))
2320 return setError(E_INVALIDARG, tr("No file to remove specified"));
2321
2322 AutoCaller autoCaller(this);
2323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2324
2325 int rc = VINF_SUCCESS;
2326
2327 try /* Can this be done without exceptions? */
2328 {
2329 Utf8Str strName;
2330 if (RT_FAILURE(objectCreateTempInternal(Utf8Str(aTemplate),
2331 Utf8Str(aPath),
2332 true, strName, &rc)))
2333 return E_FAIL;
2334 HRESULT hrc = rc == VERR_INVALID_PARAMETER ? E_INVALIDARG
2335 : rc == VERR_NOT_SUPPORTED ? VBOX_E_NOT_SUPPORTED
2336 : RT_FAILURE(rc) ? VBOX_E_IPRT_ERROR
2337 : S_OK;
2338 if (FAILED(hrc))
2339 return setError(hrc, tr("Temporary directory creation failed: %Rrc"),
2340 rc);
2341 strName.cloneTo(aDirectory);
2342 return S_OK;
2343 }
2344 catch (...)
2345 {
2346 return E_OUTOFMEMORY;
2347 }
2348#endif /* VBOX_WITH_GUEST_CONTROL */
2349}
2350
2351STDMETHODIMP GuestSession::DirectoryExists(IN_BSTR aPath, BOOL *aExists)
2352{
2353#ifndef VBOX_WITH_GUEST_CONTROL
2354 ReturnComNotImplemented();
2355#else
2356 LogFlowThisFuncEnter();
2357
2358 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2359 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
2360 CheckComArgOutPointerValid(aExists);
2361
2362 AutoCaller autoCaller(this);
2363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2364
2365 HRESULT hr = S_OK;
2366
2367 GuestFsObjData objData;
2368 int rc = fileQueryInfoInternal(Utf8Str(aPath), objData);
2369 if (RT_SUCCESS(rc))
2370 {
2371 *aExists = objData.mType == FsObjType_Directory;
2372 }
2373 else
2374 {
2375 switch (rc)
2376 {
2377 /** @todo Add more errors here! */
2378
2379 default:
2380 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence failed: %Rrc"), rc);
2381 break;
2382 }
2383 }
2384
2385 return hr;
2386#endif /* VBOX_WITH_GUEST_CONTROL */
2387}
2388
2389STDMETHODIMP GuestSession::DirectoryOpen(IN_BSTR aPath, IN_BSTR aFilter, ComSafeArrayIn(DirectoryOpenFlag_T, aFlags), IGuestDirectory **aDirectory)
2390{
2391#ifndef VBOX_WITH_GUEST_CONTROL
2392 ReturnComNotImplemented();
2393#else
2394 LogFlowThisFuncEnter();
2395
2396 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2397 return setError(E_INVALIDARG, tr("No directory to open specified"));
2398 if (RT_UNLIKELY((aFilter) != NULL && *(aFilter) != '\0'))
2399 return setError(E_INVALIDARG, tr("Directory filters not implemented yet"));
2400
2401 CheckComArgOutPointerValid(aDirectory);
2402
2403 AutoCaller autoCaller(this);
2404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2405
2406 uint32_t fFlags = DirectoryOpenFlag_None;
2407 if (aFlags)
2408 {
2409 com::SafeArray<DirectoryOpenFlag_T> flags(ComSafeArrayInArg(aFlags));
2410 for (size_t i = 0; i < flags.size(); i++)
2411 fFlags |= flags[i];
2412
2413 if (fFlags)
2414 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
2415 }
2416
2417 HRESULT hr = S_OK;
2418
2419 ComObjPtr <GuestDirectory> pDirectory;
2420 int rc = directoryOpenInternal(Utf8Str(aPath), Utf8Str(aFilter), fFlags, pDirectory);
2421 if (RT_SUCCESS(rc))
2422 {
2423 if (aDirectory)
2424 {
2425 /* Return directory object to the caller. */
2426 hr = pDirectory.queryInterfaceTo(aDirectory);
2427 }
2428 else
2429 {
2430 rc = directoryClose(pDirectory);
2431 if (RT_FAILURE(rc))
2432 hr = setError(VBOX_E_IPRT_ERROR, tr("Unable to close directory object, rc=%Rrc"), rc);
2433 }
2434 }
2435 else
2436 {
2437 switch (rc)
2438 {
2439 case VERR_INVALID_PARAMETER:
2440 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory failed: Invalid parameters given"));
2441 break;
2442
2443 case VERR_BROKEN_PIPE:
2444 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory failed: Unexpectedly aborted"));
2445 break;
2446
2447 default:
2448 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory failed: %Rrc"), rc);
2449 break;
2450 }
2451 }
2452
2453 return hr;
2454#endif /* VBOX_WITH_GUEST_CONTROL */
2455}
2456
2457STDMETHODIMP GuestSession::DirectoryQueryInfo(IN_BSTR aPath, IGuestFsObjInfo **aInfo)
2458{
2459#ifndef VBOX_WITH_GUEST_CONTROL
2460 ReturnComNotImplemented();
2461#else
2462 LogFlowThisFuncEnter();
2463
2464 AutoCaller autoCaller(this);
2465 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2466
2467 ReturnComNotImplemented();
2468#endif /* VBOX_WITH_GUEST_CONTROL */
2469}
2470
2471STDMETHODIMP GuestSession::DirectoryRemove(IN_BSTR aPath)
2472{
2473#ifndef VBOX_WITH_GUEST_CONTROL
2474 ReturnComNotImplemented();
2475#else
2476 LogFlowThisFuncEnter();
2477
2478 AutoCaller autoCaller(this);
2479 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2480
2481 ReturnComNotImplemented();
2482#endif /* VBOX_WITH_GUEST_CONTROL */
2483}
2484
2485STDMETHODIMP GuestSession::DirectoryRemoveRecursive(IN_BSTR aPath, ComSafeArrayIn(DirectoryRemoveRecFlag_T, aFlags), IProgress **aProgress)
2486{
2487#ifndef VBOX_WITH_GUEST_CONTROL
2488 ReturnComNotImplemented();
2489#else
2490 LogFlowThisFuncEnter();
2491
2492 AutoCaller autoCaller(this);
2493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2494
2495 ReturnComNotImplemented();
2496#endif /* VBOX_WITH_GUEST_CONTROL */
2497}
2498
2499STDMETHODIMP GuestSession::DirectoryRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(PathRenameFlag_T, aFlags))
2500{
2501#ifndef VBOX_WITH_GUEST_CONTROL
2502 ReturnComNotImplemented();
2503#else
2504 LogFlowThisFuncEnter();
2505
2506 AutoCaller autoCaller(this);
2507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2508
2509 ReturnComNotImplemented();
2510#endif /* VBOX_WITH_GUEST_CONTROL */
2511}
2512
2513STDMETHODIMP GuestSession::DirectorySetACL(IN_BSTR aPath, IN_BSTR aACL)
2514{
2515#ifndef VBOX_WITH_GUEST_CONTROL
2516 ReturnComNotImplemented();
2517#else
2518 LogFlowThisFuncEnter();
2519
2520 AutoCaller autoCaller(this);
2521 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2522
2523 ReturnComNotImplemented();
2524#endif /* VBOX_WITH_GUEST_CONTROL */
2525}
2526
2527STDMETHODIMP GuestSession::EnvironmentClear(void)
2528{
2529#ifndef VBOX_WITH_GUEST_CONTROL
2530 ReturnComNotImplemented();
2531#else
2532 LogFlowThisFuncEnter();
2533
2534 AutoCaller autoCaller(this);
2535 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2536
2537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2538
2539 mData.mEnvironment.Clear();
2540
2541 LogFlowFuncLeaveRC(S_OK);
2542 return S_OK;
2543#endif /* VBOX_WITH_GUEST_CONTROL */
2544}
2545
2546STDMETHODIMP GuestSession::EnvironmentGet(IN_BSTR aName, BSTR *aValue)
2547{
2548#ifndef VBOX_WITH_GUEST_CONTROL
2549 ReturnComNotImplemented();
2550#else
2551 LogFlowThisFuncEnter();
2552
2553 if (RT_UNLIKELY((aName) == NULL || *(aName) == '\0'))
2554 return setError(E_INVALIDARG, tr("No value name specified"));
2555
2556 CheckComArgOutPointerValid(aValue);
2557
2558 AutoCaller autoCaller(this);
2559 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2560
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 Bstr strValue(mData.mEnvironment.Get(Utf8Str(aName)));
2564 strValue.cloneTo(aValue);
2565
2566 LogFlowFuncLeaveRC(S_OK);
2567 return S_OK;
2568#endif /* VBOX_WITH_GUEST_CONTROL */
2569}
2570
2571STDMETHODIMP GuestSession::EnvironmentSet(IN_BSTR aName, IN_BSTR aValue)
2572{
2573#ifndef VBOX_WITH_GUEST_CONTROL
2574 ReturnComNotImplemented();
2575#else
2576 LogFlowThisFuncEnter();
2577
2578 if (RT_UNLIKELY((aName) == NULL || *(aName) == '\0'))
2579 return setError(E_INVALIDARG, tr("No value name specified"));
2580
2581 AutoCaller autoCaller(this);
2582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2583
2584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2585
2586 int rc = mData.mEnvironment.Set(Utf8Str(aName), Utf8Str(aValue));
2587
2588 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
2589 LogFlowFuncLeaveRC(hr);
2590 return hr;
2591#endif /* VBOX_WITH_GUEST_CONTROL */
2592}
2593
2594STDMETHODIMP GuestSession::EnvironmentUnset(IN_BSTR aName)
2595{
2596#ifndef VBOX_WITH_GUEST_CONTROL
2597 ReturnComNotImplemented();
2598#else
2599 LogFlowThisFuncEnter();
2600
2601 AutoCaller autoCaller(this);
2602 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2603
2604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2605
2606 mData.mEnvironment.Unset(Utf8Str(aName));
2607
2608 LogFlowFuncLeaveRC(S_OK);
2609 return S_OK;
2610#endif /* VBOX_WITH_GUEST_CONTROL */
2611}
2612
2613STDMETHODIMP GuestSession::FileCreateTemp(IN_BSTR aTemplate, ULONG aMode, IN_BSTR aPath, BOOL aSecure, IGuestFile **aFile)
2614{
2615#ifndef VBOX_WITH_GUEST_CONTROL
2616 ReturnComNotImplemented();
2617#else
2618 LogFlowThisFuncEnter();
2619
2620 AutoCaller autoCaller(this);
2621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2622
2623 ReturnComNotImplemented();
2624#endif /* VBOX_WITH_GUEST_CONTROL */
2625}
2626
2627STDMETHODIMP GuestSession::FileExists(IN_BSTR aPath, BOOL *aExists)
2628{
2629#ifndef VBOX_WITH_GUEST_CONTROL
2630 ReturnComNotImplemented();
2631#else
2632 LogFlowThisFuncEnter();
2633
2634 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2635 return setError(E_INVALIDARG, tr("No file to check existence for specified"));
2636 CheckComArgOutPointerValid(aExists);
2637
2638 AutoCaller autoCaller(this);
2639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2640
2641 HRESULT hr = S_OK;
2642
2643 GuestFsObjData objData;
2644 int rc = fileQueryInfoInternal(Utf8Str(aPath), objData);
2645 if (RT_SUCCESS(rc))
2646 {
2647 *aExists = objData.mType == FsObjType_File;
2648 }
2649 else
2650 {
2651 switch (rc)
2652 {
2653 /** @todo Add more errors here! */
2654
2655 default:
2656 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file existence failed: %Rrc"), rc);
2657 break;
2658 }
2659 }
2660
2661 return hr;
2662#endif /* VBOX_WITH_GUEST_CONTROL */
2663}
2664
2665STDMETHODIMP GuestSession::FileRemove(IN_BSTR aPath)
2666{
2667#ifndef VBOX_WITH_GUEST_CONTROL
2668 ReturnComNotImplemented();
2669#else
2670 LogFlowThisFuncEnter();
2671
2672 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2673 return setError(E_INVALIDARG, tr("No file to remove specified"));
2674
2675 AutoCaller autoCaller(this);
2676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2677
2678 try /* Can this be done without exceptions? */
2679 {
2680 int rc2;
2681 int rc = fileRemoveInternal(Utf8Str(aPath), &rc2);
2682 if (RT_FAILURE((rc)))
2683 return setError(E_FAIL,
2684 tr("Internal error deleting file: %Rrc"), rc);
2685 else if (RT_FAILURE((rc2)))
2686 return setError(VBOX_E_IPRT_ERROR,
2687 tr("File deletion on guest returned: %Rrc"), rc2);
2688 }
2689 catch (...)
2690 {
2691 return E_OUTOFMEMORY;
2692 }
2693 return S_OK;
2694#endif /* VBOX_WITH_GUEST_CONTROL */
2695}
2696
2697STDMETHODIMP GuestSession::FileOpen(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, ULONG aCreationMode, LONG64 aOffset, IGuestFile **aFile)
2698{
2699#ifndef VBOX_WITH_GUEST_CONTROL
2700 ReturnComNotImplemented();
2701#else
2702 LogFlowThisFuncEnter();
2703
2704 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2705 return setError(E_INVALIDARG, tr("No file to open specified"));
2706 if (RT_UNLIKELY((aOpenMode) == NULL || *(aOpenMode) == '\0'))
2707 return setError(E_INVALIDARG, tr("No open mode specified"));
2708 if (RT_UNLIKELY((aDisposition) == NULL || *(aDisposition) == '\0'))
2709 return setError(E_INVALIDARG, tr("No disposition mode specified"));
2710
2711 CheckComArgOutPointerValid(aFile);
2712
2713 AutoCaller autoCaller(this);
2714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2715
2716 /** @todo Validate open mode. */
2717 /** @todo Validate disposition mode. */
2718
2719 /** @todo Validate creation mode. */
2720 uint32_t uCreationMode = 0;
2721
2722 HRESULT hr = S_OK;
2723
2724 ComObjPtr <GuestFile> pFile;
2725 int rc = fileOpenInternal(Utf8Str(aPath), Utf8Str(aOpenMode), Utf8Str(aDisposition),
2726 aCreationMode, aOffset, pFile);
2727 if (RT_SUCCESS(rc))
2728 {
2729 /* Return directory object to the caller. */
2730 hr = pFile.queryInterfaceTo(aFile);
2731 }
2732 else
2733 {
2734 switch (rc)
2735 {
2736 /** @todo Add more error info! */
2737
2738 default:
2739 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
2740 break;
2741 }
2742 }
2743
2744 return hr;
2745#endif /* VBOX_WITH_GUEST_CONTROL */
2746}
2747
2748STDMETHODIMP GuestSession::FileQueryInfo(IN_BSTR aPath, IGuestFsObjInfo **aInfo)
2749{
2750#ifndef VBOX_WITH_GUEST_CONTROL
2751 ReturnComNotImplemented();
2752#else
2753 LogFlowThisFuncEnter();
2754
2755 AutoCaller autoCaller(this);
2756 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2757
2758 ReturnComNotImplemented();
2759#endif /* VBOX_WITH_GUEST_CONTROL */
2760}
2761
2762STDMETHODIMP GuestSession::FileQuerySize(IN_BSTR aPath, LONG64 *aSize)
2763{
2764#ifndef VBOX_WITH_GUEST_CONTROL
2765 ReturnComNotImplemented();
2766#else
2767 LogFlowThisFuncEnter();
2768
2769 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2770 return setError(E_INVALIDARG, tr("No file to query size for specified"));
2771 CheckComArgOutPointerValid(aSize);
2772
2773 AutoCaller autoCaller(this);
2774 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2775
2776 HRESULT hr = S_OK;
2777
2778 int64_t llSize;
2779 int rc = fileQuerySizeInternal(Utf8Str(aPath), &llSize);
2780 if (RT_SUCCESS(rc))
2781 {
2782 *aSize = llSize;
2783 }
2784 else
2785 {
2786 switch (rc)
2787 {
2788 /** @todo Add more errors here! */
2789
2790 default:
2791 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file size failed: %Rrc"), rc);
2792 break;
2793 }
2794 }
2795
2796 return hr;
2797#endif /* VBOX_WITH_GUEST_CONTROL */
2798}
2799
2800STDMETHODIMP GuestSession::FileRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(PathRenameFlag_T, aFlags))
2801{
2802#ifndef VBOX_WITH_GUEST_CONTROL
2803 ReturnComNotImplemented();
2804#else
2805 LogFlowThisFuncEnter();
2806
2807 AutoCaller autoCaller(this);
2808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2809
2810 ReturnComNotImplemented();
2811#endif /* VBOX_WITH_GUEST_CONTROL */
2812}
2813
2814STDMETHODIMP GuestSession::FileSetACL(IN_BSTR aPath, IN_BSTR aACL)
2815{
2816#ifndef VBOX_WITH_GUEST_CONTROL
2817 ReturnComNotImplemented();
2818#else
2819 LogFlowThisFuncEnter();
2820
2821 AutoCaller autoCaller(this);
2822 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2823
2824 ReturnComNotImplemented();
2825#endif /* VBOX_WITH_GUEST_CONTROL */
2826}
2827
2828STDMETHODIMP GuestSession::ProcessCreate(IN_BSTR aCommand, ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
2829 ComSafeArrayIn(ProcessCreateFlag_T, aFlags), ULONG aTimeoutMS, IGuestProcess **aProcess)
2830{
2831#ifndef VBOX_WITH_GUEST_CONTROL
2832 ReturnComNotImplemented();
2833#else
2834 LogFlowThisFuncEnter();
2835
2836 com::SafeArray<LONG> affinity;
2837
2838 HRESULT hr = ProcessCreateEx(aCommand, ComSafeArrayInArg(aArguments), ComSafeArrayInArg(aEnvironment),
2839 ComSafeArrayInArg(aFlags), aTimeoutMS, ProcessPriority_Default, ComSafeArrayAsInParam(affinity), aProcess);
2840 return hr;
2841#endif /* VBOX_WITH_GUEST_CONTROL */
2842}
2843
2844STDMETHODIMP GuestSession::ProcessCreateEx(IN_BSTR aCommand, ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
2845 ComSafeArrayIn(ProcessCreateFlag_T, aFlags), ULONG aTimeoutMS,
2846 ProcessPriority_T aPriority, ComSafeArrayIn(LONG, aAffinity),
2847 IGuestProcess **aProcess)
2848{
2849#ifndef VBOX_WITH_GUEST_CONTROL
2850 ReturnComNotImplemented();
2851#else
2852 LogFlowThisFuncEnter();
2853
2854 if (RT_UNLIKELY((aCommand) == NULL || *(aCommand) == '\0'))
2855 return setError(E_INVALIDARG, tr("No command to execute specified"));
2856 CheckComArgOutPointerValid(aProcess);
2857
2858 AutoCaller autoCaller(this);
2859 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2860
2861 GuestProcessStartupInfo procInfo;
2862 procInfo.mCommand = Utf8Str(aCommand);
2863
2864 if (aArguments)
2865 {
2866 com::SafeArray<IN_BSTR> arguments(ComSafeArrayInArg(aArguments));
2867 for (size_t i = 0; i < arguments.size(); i++)
2868 procInfo.mArguments.push_back(Utf8Str(arguments[i]));
2869 }
2870
2871 int rc = VINF_SUCCESS;
2872
2873 /*
2874 * Create the process environment:
2875 * - Apply the session environment in a first step, and
2876 * - Apply environment variables specified by this call to
2877 * have the chance of overwriting/deleting session entries.
2878 */
2879 procInfo.mEnvironment = mData.mEnvironment; /* Apply original session environment. */
2880
2881 if (aEnvironment)
2882 {
2883 com::SafeArray<IN_BSTR> environment(ComSafeArrayInArg(aEnvironment));
2884 for (size_t i = 0; i < environment.size() && RT_SUCCESS(rc); i++)
2885 rc = procInfo.mEnvironment.Set(Utf8Str(environment[i]));
2886 }
2887
2888 HRESULT hr = S_OK;
2889
2890 if (RT_SUCCESS(rc))
2891 {
2892 if (aFlags)
2893 {
2894 com::SafeArray<ProcessCreateFlag_T> flags(ComSafeArrayInArg(aFlags));
2895 for (size_t i = 0; i < flags.size(); i++)
2896 procInfo.mFlags |= flags[i];
2897 }
2898
2899 procInfo.mTimeoutMS = aTimeoutMS;
2900
2901 if (aAffinity)
2902 {
2903 com::SafeArray<LONG> affinity(ComSafeArrayInArg(aAffinity));
2904 for (size_t i = 0; i < affinity.size(); i++)
2905 procInfo.mAffinity[i] = affinity[i]; /** @todo Really necessary? Later. */
2906 }
2907
2908 procInfo.mPriority = aPriority;
2909
2910 ComObjPtr<GuestProcess> pProcess;
2911 rc = processCreateExInteral(procInfo, pProcess);
2912 if (RT_SUCCESS(rc))
2913 {
2914 /* Return guest session to the caller. */
2915 HRESULT hr2 = pProcess.queryInterfaceTo(aProcess);
2916 if (FAILED(hr2))
2917 rc = VERR_COM_OBJECT_NOT_FOUND;
2918
2919 if (RT_SUCCESS(rc))
2920 rc = pProcess->startProcessAsync();
2921 }
2922 }
2923
2924 if (RT_FAILURE(rc))
2925 {
2926 switch (rc)
2927 {
2928 case VERR_MAX_PROCS_REACHED:
2929 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest processes per session (%ld) reached"),
2930 VBOX_GUESTCTRL_MAX_PROCESSES);
2931 break;
2932
2933 /** @todo Add more errors here. */
2934
2935 default:
2936 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest process, rc=%Rrc"), rc);
2937 break;
2938 }
2939 }
2940
2941 LogFlowFuncLeaveRC(rc);
2942 return hr;
2943#endif /* VBOX_WITH_GUEST_CONTROL */
2944}
2945
2946STDMETHODIMP GuestSession::ProcessGet(ULONG aPID, IGuestProcess **aProcess)
2947{
2948#ifndef VBOX_WITH_GUEST_CONTROL
2949 ReturnComNotImplemented();
2950#else
2951 LogFlowThisFunc(("aPID=%RU32\n", aPID));
2952
2953 CheckComArgOutPointerValid(aProcess);
2954 if (aPID == 0)
2955 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
2956
2957 AutoCaller autoCaller(this);
2958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2959
2960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2961
2962 HRESULT hr = S_OK;
2963
2964 ComObjPtr<GuestProcess> pProcess;
2965 int rc = processGetByPID(aPID, &pProcess);
2966 if (RT_FAILURE(rc))
2967 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPID);
2968
2969 /* This will set (*aProcess) to NULL if pProgress is NULL. */
2970 HRESULT hr2 = pProcess.queryInterfaceTo(aProcess);
2971 if (SUCCEEDED(hr))
2972 hr = hr2;
2973
2974 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", *aProcess, hr));
2975 return hr;
2976#endif /* VBOX_WITH_GUEST_CONTROL */
2977}
2978
2979STDMETHODIMP GuestSession::SymlinkCreate(IN_BSTR aSource, IN_BSTR aTarget, SymlinkType_T aType)
2980{
2981#ifndef VBOX_WITH_GUEST_CONTROL
2982 ReturnComNotImplemented();
2983#else
2984 LogFlowThisFuncEnter();
2985
2986 AutoCaller autoCaller(this);
2987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2988
2989 ReturnComNotImplemented();
2990#endif /* VBOX_WITH_GUEST_CONTROL */
2991}
2992
2993STDMETHODIMP GuestSession::SymlinkExists(IN_BSTR aSymlink, BOOL *aExists)
2994{
2995#ifndef VBOX_WITH_GUEST_CONTROL
2996 ReturnComNotImplemented();
2997#else
2998 LogFlowThisFuncEnter();
2999
3000 AutoCaller autoCaller(this);
3001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3002
3003 ReturnComNotImplemented();
3004#endif /* VBOX_WITH_GUEST_CONTROL */
3005}
3006
3007STDMETHODIMP GuestSession::SymlinkRead(IN_BSTR aSymlink, ComSafeArrayIn(SymlinkReadFlag_T, aFlags), BSTR *aTarget)
3008{
3009#ifndef VBOX_WITH_GUEST_CONTROL
3010 ReturnComNotImplemented();
3011#else
3012 LogFlowThisFuncEnter();
3013
3014 AutoCaller autoCaller(this);
3015 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3016
3017 ReturnComNotImplemented();
3018#endif /* VBOX_WITH_GUEST_CONTROL */
3019}
3020
3021STDMETHODIMP GuestSession::SymlinkRemoveDirectory(IN_BSTR aPath)
3022{
3023#ifndef VBOX_WITH_GUEST_CONTROL
3024 ReturnComNotImplemented();
3025#else
3026 LogFlowThisFuncEnter();
3027
3028 AutoCaller autoCaller(this);
3029 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3030
3031 ReturnComNotImplemented();
3032#endif /* VBOX_WITH_GUEST_CONTROL */
3033}
3034
3035STDMETHODIMP GuestSession::SymlinkRemoveFile(IN_BSTR aFile)
3036{
3037#ifndef VBOX_WITH_GUEST_CONTROL
3038 ReturnComNotImplemented();
3039#else
3040 LogFlowThisFuncEnter();
3041
3042 AutoCaller autoCaller(this);
3043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3044
3045 ReturnComNotImplemented();
3046#endif /* VBOX_WITH_GUEST_CONTROL */
3047}
3048
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette