VirtualBox

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

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

Guest Control 2.0: Update.

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