VirtualBox

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

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

Main: split out implementation of GuestSession:DirectoryMakeTemp and added (disabled) test.

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