VirtualBox

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

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