VirtualBox

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

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

Main: adjusted and documented IGuestSession::(File|Directory)CreateTemp APIs.

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