VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/HostUpdateImpl.cpp@ 85736

Last change on this file since 85736 was 85736, checked in by vboxsync, 5 years ago

Main/HostUpdateImpl.cpp: Better (but completely untested) server response validation. Make sure we cannot throw exceptions before we call RTHttpFreeResponse. bugref:7983

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.9 KB
Line 
1/* $Id: HostUpdateImpl.cpp 85736 2020-08-12 21:11:37Z vboxsync $ */
2/** @file
3 * IHostUpdate COM class implementations.
4 */
5
6/*
7 * Copyright (C) 2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19#define LOG_GROUP LOG_GROUP_MAIN_HOSTUPDATE
20
21#include <iprt/cpp/utils.h>
22#include <iprt/param.h>
23#include <iprt/path.h>
24#include <iprt/http.h>
25#include <iprt/system.h>
26#include <iprt/message.h>
27#include <iprt/pipe.h>
28#include <iprt/env.h>
29#include <iprt/process.h>
30#include <iprt/assert.h>
31#include <iprt/err.h>
32#include <iprt/stream.h>
33#include <iprt/time.h>
34#include <VBox/com/defs.h>
35#include <VBox/version.h>
36
37#include "HostImpl.h"
38#include "HostUpdateImpl.h"
39#include "ProgressImpl.h"
40#include "AutoCaller.h"
41#include "LoggingNew.h"
42#include "VirtualBoxImpl.h"
43#include "ThreadTask.h"
44#include "SystemPropertiesImpl.h"
45#include "VirtualBoxBase.h"
46
47
48////////////////////////////////////////////////////////////////////////////////
49//
50// HostUpdate private data definition
51//
52////////////////////////////////////////////////////////////////////////////////
53
54
55class HostUpdate::UpdateCheckTask : public ThreadTask
56{
57public:
58 UpdateCheckTask(UpdateCheckType_T aCheckType, HostUpdate *aThat, Progress *aProgress)
59 : m_checkType(aCheckType)
60 , m_pHostUpdate(aThat)
61 , m_ptrProgress(aProgress)
62 {
63 m_strTaskName = "UpdateCheckTask";
64 }
65 ~UpdateCheckTask() { }
66
67private:
68 void handler();
69
70 UpdateCheckType_T m_checkType;
71 HostUpdate *m_pHostUpdate;
72
73 /** Smart pointer to the progress object for this job. */
74 ComObjPtr<Progress> m_ptrProgress;
75
76 friend class HostUpdate; // allow member functions access to private data
77};
78
79void HostUpdate::UpdateCheckTask::handler()
80{
81 HostUpdate *pHostUpdate = this->m_pHostUpdate;
82
83 LogFlowFuncEnter();
84 LogFlowFunc(("HostUpdate %p\n", pHostUpdate));
85
86 HRESULT rc = pHostUpdate->i_updateCheckTask(this);
87
88 LogFlowFunc(("rc=%Rhrc\n", rc)); NOREF(rc);
89 LogFlowFuncLeave();
90}
91
92Utf8Str HostUpdate::i_platformInfo()
93{
94 /* Prepare platform report: */
95 Utf8Str strPlatform;
96
97#if defined (RT_OS_WINDOWS)
98 strPlatform = "win";
99#elif defined (RT_OS_LINUX)
100 strPlatform = "linux";
101#elif defined (RT_OS_DARWIN)
102 strPlatform = "macosx";
103#elif defined (RT_OS_OS2)
104 strPlatform = "os2";
105#elif defined (RT_OS_FREEBSD)
106 strPlatform = "freebsd";
107#elif defined (RT_OS_SOLARIS)
108 strPlatform = "solaris";
109#else
110 strPlatform = "unknown";
111#endif
112
113 /* The format is <system>.<bitness>: */
114 strPlatform.appendPrintf(".%lu", ARCH_BITS);
115
116 /* Add more system information: */
117 int vrc;
118#ifdef RT_OS_LINUX
119 // WORKAROUND:
120 // On Linux we try to generate information using script first of all..
121
122 /* Get script path: */
123 char szAppPrivPath[RTPATH_MAX];
124 vrc = RTPathAppPrivateNoArch(szAppPrivPath, sizeof(szAppPrivPath));
125 AssertRC(vrc);
126 if (RT_SUCCESS(vrc))
127 vrc = RTPathAppend(szAppPrivPath, sizeof(szAppPrivPath), "/VBoxSysInfo.sh");
128 AssertRC(vrc);
129 if (RT_SUCCESS(vrc))
130 {
131 RTPIPE hPipeR;
132 RTHANDLE hStdOutPipe;
133 hStdOutPipe.enmType = RTHANDLETYPE_PIPE;
134 vrc = RTPipeCreate(&hPipeR, &hStdOutPipe.u.hPipe, RTPIPE_C_INHERIT_WRITE);
135 AssertLogRelRC(vrc);
136
137 char const *szAppPrivArgs[2];
138 szAppPrivArgs[0] = szAppPrivPath;
139 szAppPrivArgs[1] = NULL;
140 RTPROCESS hProc = NIL_RTPROCESS;
141
142 /* Run script: */
143 vrc = RTProcCreateEx(szAppPrivPath, szAppPrivArgs, RTENV_DEFAULT, 0 /*fFlags*/, NULL /*phStdin*/, &hStdOutPipe,
144 NULL /*phStderr*/, NULL /*pszAsUser*/, NULL /*pszPassword*/, NULL /*pvExtraData*/, &hProc);
145
146 (void) RTPipeClose(hStdOutPipe.u.hPipe);
147 hStdOutPipe.u.hPipe = NIL_RTPIPE;
148
149 if (RT_SUCCESS(vrc))
150 {
151 RTPROCSTATUS ProcStatus;
152 size_t cbStdOutBuf = 0;
153 size_t offStdOutBuf = 0;
154 char *pszStdOutBuf = NULL;
155 do
156 {
157 if (hPipeR != NIL_RTPIPE)
158 {
159 char achBuf[1024];
160 size_t cbRead;
161 vrc = RTPipeReadBlocking(hPipeR, achBuf, sizeof(achBuf), &cbRead);
162 if (RT_SUCCESS(vrc))
163 {
164 /* grow the buffer? */
165 size_t cbBufReq = offStdOutBuf + cbRead + 1;
166 if ( cbBufReq > cbStdOutBuf
167 && cbBufReq < _256K)
168 {
169 size_t cbNew = RT_ALIGN_Z(cbBufReq, 16); // 1024
170 void *pvNew = RTMemRealloc(pszStdOutBuf, cbNew);
171 if (pvNew)
172 {
173 pszStdOutBuf = (char *)pvNew;
174 cbStdOutBuf = cbNew;
175 }
176 }
177
178 /* append if we've got room. */
179 if (cbBufReq <= cbStdOutBuf)
180 {
181 (void) memcpy(&pszStdOutBuf[offStdOutBuf], achBuf, cbRead);
182 offStdOutBuf = offStdOutBuf + cbRead;
183 pszStdOutBuf[offStdOutBuf] = '\0';
184 }
185 }
186 else
187 {
188 AssertLogRelMsg(vrc == VERR_BROKEN_PIPE, ("%Rrc\n", vrc));
189 RTPipeClose(hPipeR);
190 hPipeR = NIL_RTPIPE;
191 }
192 }
193
194 /*
195 * Service the process. Block if we have no pipe.
196 */
197 if (hProc != NIL_RTPROCESS)
198 {
199 vrc = RTProcWait(hProc,
200 hPipeR == NIL_RTPIPE ? RTPROCWAIT_FLAGS_BLOCK : RTPROCWAIT_FLAGS_NOBLOCK,
201 &ProcStatus);
202 if (RT_SUCCESS(vrc))
203 hProc = NIL_RTPROCESS;
204 else
205 AssertLogRelMsgStmt(vrc == VERR_PROCESS_RUNNING, ("%Rrc\n", vrc), hProc = NIL_RTPROCESS);
206 }
207 } while ( hPipeR != NIL_RTPIPE
208 || hProc != NIL_RTPROCESS);
209
210 if ( ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
211 && ProcStatus.iStatus == 0) {
212 pszStdOutBuf[offStdOutBuf-1] = '\0'; // remove trailing newline
213 Utf8Str pszStdOutBufUTF8(pszStdOutBuf);
214 strPlatform.appendPrintf(" [%s]", pszStdOutBufUTF8.strip().c_str());
215 // For testing, here is some sample output:
216 //strPlatform.appendPrintf(" [Distribution: Redhat | Version: 7.6.1810 | Kernel: Linux version 3.10.0-952.27.2.el7.x86_64 (gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) ) #1 SMP Mon Jul 29 17:46:05 UTC 2019]");
217 }
218 }
219 else
220 vrc = VERR_TRY_AGAIN; /* (take the fallback path) */
221 }
222
223 LogRelFunc(("strPlatform (Linux) = %s\n", strPlatform.c_str()));
224
225 if (RT_FAILURE(vrc))
226#endif /* RT_OS_LINUX */
227 {
228 /* Use RTSystemQueryOSInfo: */
229 char szTmp[256];
230
231 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
232 if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
233 strPlatform.appendPrintf(" [Product: %s", szTmp);
234
235 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
236 if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
237 strPlatform.appendPrintf(" %sRelease: %s", strlen(szTmp) == 0 ? "[" : "| ", szTmp);
238
239 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
240 if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
241 strPlatform.appendPrintf(" %sVersion: %s", strlen(szTmp) == 0 ? "[" : "| ", szTmp);
242
243 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
244 if ((RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) && szTmp[0] != '\0')
245 strPlatform.appendPrintf(" %sSP: %s]", strlen(szTmp) == 0 ? "[" : "| ", szTmp);
246
247 if (!strPlatform.endsWith("]"))
248 strPlatform.append("]");
249
250 LogRelFunc(("strPlatform = %s\n", strPlatform.c_str()));
251 }
252
253 return strPlatform;
254}
255
256HRESULT HostUpdate::i_checkForVBoxUpdate()
257{
258 HRESULT rc;
259
260 // Default to no update required
261 m_updateNeeded = FALSE;
262
263 // Following the sequence of steps in UIUpdateStepVirtualBox::sltStartStep()
264 // Build up our query URL starting with the URL basename
265 Utf8Str strUrl("https://update.virtualbox.org/query.php/?");
266 Bstr platform;
267 rc = mVirtualBox->COMGETTER(PackageType)(platform.asOutParam());
268 if (FAILED(rc))
269 return setErrorVrc(rc, tr("%s: IVirtualBox::packageType() failed: %Rrc"), __FUNCTION__, rc);
270 strUrl.appendPrintf("platform=%ls", platform.raw()); // e.g. SOLARIS_64BITS_GENERIC
271
272 // Get the complete current version string for the query URL
273 Bstr versionNormalized;
274 rc = mVirtualBox->COMGETTER(VersionNormalized)(versionNormalized.asOutParam());
275 if (FAILED(rc))
276 return setErrorVrc(rc, tr("%s: IVirtualBox::versionNormalized() failed: %Rrc"), __FUNCTION__, rc);
277 strUrl.appendPrintf("&version=%ls", versionNormalized.raw()); // e.g. 6.1.1
278 // strUrl.appendPrintf("&version=6.0.12"); // comment out previous line and uncomment this one for testing
279
280 ULONG revision;
281 rc = mVirtualBox->COMGETTER(Revision)(&revision);
282 if (FAILED(rc))
283 return setErrorVrc(rc, tr("%s: IVirtualBox::revision() failed: %Rrc"), __FUNCTION__, rc);
284 strUrl.appendPrintf("_%u", revision); // e.g. 135618
285
286 // acquire the System Properties interface
287 ComPtr<ISystemProperties> ptrSystemProperties;
288 rc = mVirtualBox->COMGETTER(SystemProperties)(ptrSystemProperties.asOutParam());
289 if (FAILED(rc))
290 return setErrorVrc(rc, tr("%s: IVirtualBox::systemProperties() failed: %Rrc"), __FUNCTION__, rc);
291
292 // Update the VBoxUpdate setting 'VBoxUpdateLastCheckDate'
293 RTTIME Time;
294 RTTIMESPEC TimeNow;
295 char szTimeStr[RTTIME_STR_LEN];
296
297 RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&TimeNow)), szTimeStr, sizeof(szTimeStr));
298 LogRelFunc(("VBox updating UpdateDate with TimeString = %s\n", szTimeStr));
299 rc = ptrSystemProperties->COMSETTER(VBoxUpdateLastCheckDate)(Bstr(szTimeStr).raw());
300 if (FAILED(rc))
301 return rc; // ISystemProperties::setLastCheckDate calls setError() on failure
302
303 // Update the queryURL and the VBoxUpdate setting 'VBoxUpdateCount'
304 ULONG cVBoxUpdateCount = 0;
305 rc = ptrSystemProperties->COMGETTER(VBoxUpdateCount)(&cVBoxUpdateCount);
306 if (FAILED(rc))
307 return setErrorVrc(rc, tr("%s: retrieving ISystemProperties::VBoxUpdateCount failed: %Rrc"), __FUNCTION__, rc);
308
309 cVBoxUpdateCount++;
310
311 rc = ptrSystemProperties->COMSETTER(VBoxUpdateCount)(cVBoxUpdateCount);
312 if (FAILED(rc))
313 return rc; // ISystemProperties::setVBoxUpdateCount calls setError() on failure
314 strUrl.appendPrintf("&count=%u", cVBoxUpdateCount);
315
316 // Update the query URL and the VBoxUpdate settings (if necessary) with the 'Target' information.
317 VBoxUpdateTarget_T enmTarget = VBoxUpdateTarget_Stable; // default branch is 'stable'
318 rc = ptrSystemProperties->COMGETTER(VBoxUpdateTarget)(&enmTarget);
319 if (FAILED(rc))
320 return setErrorVrc(rc, tr("%s: retrieving ISystemProperties::Target failed: %Rrc"), __FUNCTION__, rc);
321
322 switch (enmTarget)
323 {
324 case VBoxUpdateTarget_AllReleases:
325 strUrl.appendPrintf("&branch=allrelease"); // query.php expects 'allrelease' and not 'allreleases'
326 break;
327 case VBoxUpdateTarget_WithBetas:
328 strUrl.appendPrintf("&branch=withbetas");
329 break;
330 case VBoxUpdateTarget_Stable:
331 default:
332 strUrl.appendPrintf("&branch=stable");
333 break;
334 }
335
336 rc = ptrSystemProperties->COMSETTER(VBoxUpdateTarget)(enmTarget);
337 if (FAILED(rc))
338 return rc; // ISystemProperties::setTarget calls setError() on failure
339
340 LogRelFunc(("VBox update URL = %s\n", strUrl.c_str()));
341
342 /*
343 * Compose the User-Agent header for the GET request.
344 */
345 Bstr version;
346 rc = mVirtualBox->COMGETTER(Version)(version.asOutParam()); // e.g. 6.1.0_RC1
347 if (FAILED(rc))
348 return setErrorVrc(rc, tr("%s: IVirtualBox::version() failed: %Rrc"), __FUNCTION__, rc);
349
350 Utf8StrFmt const strUserAgent("VirtualBox %ls <%s>", version.raw(), HostUpdate::i_platformInfo().c_str());
351 LogRelFunc(("userAgent = %s\n", strUserAgent.c_str()));
352
353 /*
354 * Create the HTTP client instance and pass it to a inner worker method to
355 * ensure proper cleanup.
356 */
357 RTHTTP hHttp = NIL_RTHTTP;
358 int vrc = RTHttpCreate(&hHttp);
359 if (RT_SUCCESS(vrc))
360 {
361 try
362 {
363 rc = i_checkForVBoxUpdateInner(hHttp, strUrl, strUserAgent, ptrSystemProperties);
364 }
365 catch (...)
366 {
367 AssertFailed();
368 rc = E_UNEXPECTED;
369 }
370 RTHttpDestroy(hHttp);
371 }
372 else
373 rc = setErrorVrc(vrc, tr("%s: RTHttpCreate() failed: %Rrc"), __FUNCTION__, vrc);
374 return S_OK;
375}
376
377HRESULT HostUpdate::i_checkForVBoxUpdateInner(RTHTTP hHttp, Utf8Str const &strUrl, Utf8Str const &strUserAgent,
378 ComPtr<ISystemProperties> const &ptrSystemProperties)
379{
380 /// @todo Are there any other headers needed to be added first via RTHttpSetHeaders()?
381 int vrc = RTHttpAddHeader(hHttp, "User-Agent", strUserAgent.c_str(), strUserAgent.length(), RTHTTPADDHDR_F_BACK);
382 if (RT_FAILURE(vrc))
383 return setErrorVrc(vrc, tr("%s: RTHttpAddHeader() failed: %Rrc (on User-Agent)"), __FUNCTION__, vrc);
384
385 /*
386 * Configure proxying.
387 */
388 ProxyMode_T enmProxyMode;
389 HRESULT rc = ptrSystemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
390 if (FAILED(rc))
391 return setError(rc, tr("%s: ISystemProperties::proxyMode() failed: %Rrc"), __FUNCTION__, rc);
392
393 if (enmProxyMode == ProxyMode_Manual)
394 {
395 Bstr strProxyURL;
396 rc = ptrSystemProperties->COMGETTER(ProxyURL)(strProxyURL.asOutParam());
397 if (FAILED(rc))
398 return setError(rc, tr("%s: ISystemProperties::proxyURL() failed: %Rrc"), __FUNCTION__, rc);
399 vrc = RTHttpSetProxyByUrl(hHttp, Utf8Str(strProxyURL).c_str());
400 if (RT_FAILURE(vrc))
401 return setErrorVrc(vrc, tr("%s: RTHttpSetProxyByUrl() failed: %Rrc"), __FUNCTION__, vrc);
402 }
403 else if (enmProxyMode == ProxyMode_System)
404 {
405 vrc = RTHttpUseSystemProxySettings(hHttp);
406 if (RT_FAILURE(vrc))
407 return setErrorVrc(vrc, tr("%s: RTHttpUseSystemProxySettings() failed: %Rrc"), __FUNCTION__, vrc);
408 }
409 else
410 Assert(enmProxyMode == ProxyMode_NoProxy);
411
412 /*
413 * Perform the GET request, returning raw binary stuff.
414 */
415 void *pvResponse = NULL;
416 size_t cbResponse = 0;
417 vrc = RTHttpGetBinary(hHttp, strUrl.c_str(), &pvResponse, &cbResponse);
418 if (RT_FAILURE(vrc))
419 return setErrorVrc(vrc, tr("%s: RTHttpGetBinary() failed: %Rrc"), __FUNCTION__, vrc);
420
421 /* Note! We can do nothing that might throw exceptions till we call RTHttpFreeResponse! */
422
423 /*
424 * If url is platform=DARWIN_64BITS_GENERIC&version=6.0.12&branch=stable for example, the reply is:
425 * 6.0.14<SPACE>https://download.virtualbox.org/virtualbox/6.0.14/VirtualBox-6.0.14-133895-OSX.dmg
426 * If no update required, 'UPTODATE' is returned.
427 */
428 /* Parse out the two first words of the response, ignoring whatever follows: */
429 const char *pchResponse = (const char *)pvResponse;
430 while (cbResponse > 0 && *pchResponse == ' ')
431 cbResponse--, pchResponse++;
432
433 char ch;
434 const char *pchWord0 = pchResponse;
435 while (cbResponse > 0 && (ch = *pchResponse) != ' ' && ch != '\0')
436 cbResponse--, pchResponse++;
437 size_t const cchWord0 = (size_t)(pchResponse - pchWord0);
438
439 while (cbResponse > 0 && *pchResponse == ' ')
440 cbResponse--, pchResponse++;
441 const char *pchWord1 = pchResponse;
442 while (cbResponse > 0 && (ch = *pchResponse) != ' ' && ch != '\0')
443 cbResponse--, pchResponse++;
444 size_t const cchWord1 = (size_t)(pchResponse - pchWord1);
445
446 /* Decode the two word: */
447 static char const s_szUpToDate[] = "UPTODATE";
448 if ( cchWord0 == sizeof(s_szUpToDate) - 1
449 && memcmp(pchWord0, s_szUpToDate, sizeof(s_szUpToDate) - 1) == 0)
450 {
451 m_updateNeeded = FALSE;
452 rc = S_OK;
453 }
454 else
455 {
456 vrc = RTStrValidateEncodingEx(pchWord0, cchWord0, 0 /*fFlags*/);
457 if (RT_SUCCESS(vrc))
458 vrc = RTStrValidateEncodingEx(pchWord1, cchWord1, 0 /*fFlags*/);
459 if (RT_SUCCESS(vrc))
460 {
461 /** @todo Any additional sanity checks we could perform here? */
462 rc = m_updateVersion.assignEx(pchWord0, cchWord0);
463 if (SUCCEEDED(rc))
464 {
465 rc = m_updateVersion.assignEx(pchWord1, cchWord1);
466 if (SUCCEEDED(rc))
467 m_updateNeeded = TRUE;
468 }
469 LogRelFunc(("HTTP server reply = %.*s %.*s\n", cchWord0, pchWord0, cchWord1, pchWord1));
470 }
471 else
472 rc = setErrorVrc(vrc, tr("Invalid server response: %Rrc (%.*Rhxs -- %.*Rhxs)"),
473 vrc, cchWord0, pchWord0, cchWord1, pchWord1);
474 }
475
476 RTHttpFreeResponse(pvResponse);
477
478 return rc;
479}
480
481HRESULT HostUpdate::i_updateCheckTask(UpdateCheckTask *pTask)
482{
483 LogFlowFuncEnter();
484 AutoCaller autoCaller(this);
485 HRESULT hrc = autoCaller.rc();
486 if (SUCCEEDED(hrc))
487 {
488 try
489 {
490 switch (pTask->m_checkType)
491 {
492 case UpdateCheckType_VirtualBox:
493 hrc = i_checkForVBoxUpdate();
494 break;
495#if 0
496 case UpdateCheckType_ExtensionPack:
497 hrc = i_checkForExtPackUpdate();
498 break;
499
500 case UpdateCheckType_GuestAdditions:
501 hrc = i_checkForGuestAdditionsUpdate();
502 break;
503#endif
504 default:
505 hrc = setError(E_FAIL, tr("Update check type %d is not implemented"), pTask->m_checkType);
506 break;
507 }
508 }
509 catch (...)
510 {
511 AssertFailed();
512 hrc = E_UNEXPECTED;
513 }
514 }
515
516 if (!pTask->m_ptrProgress.isNull())
517 pTask->m_ptrProgress->i_notifyComplete(hrc);
518
519 LogFlowFunc(("rc=%Rhrc\n", hrc));
520 LogFlowFuncLeave();
521 return hrc;
522}
523
524////////////////////////////////////////////////////////////////////////////////
525//
526// HostUpdate constructor / destructor
527//
528// ////////////////////////////////////////////////////////////////////////////////
529HostUpdate::HostUpdate()
530 : mVirtualBox(NULL)
531{
532}
533
534HostUpdate::~HostUpdate()
535{
536}
537
538
539HRESULT HostUpdate::FinalConstruct()
540{
541 return BaseFinalConstruct();
542}
543
544void HostUpdate::FinalRelease()
545{
546 uninit();
547
548 BaseFinalRelease();
549}
550
551HRESULT HostUpdate::init(VirtualBox *aVirtualBox)
552{
553 // Enclose the state transition NotReady->InInit->Ready.
554 AutoInitSpan autoInitSpan(this);
555 AssertReturn(autoInitSpan.isOk(), E_FAIL);
556
557 /* Weak reference to a VirtualBox object */
558 unconst(mVirtualBox) = aVirtualBox;
559
560 autoInitSpan.setSucceeded();
561 return S_OK;
562}
563
564void HostUpdate::uninit()
565{
566 // Enclose the state transition Ready->InUninit->NotReady.
567 AutoUninitSpan autoUninitSpan(this);
568 if (autoUninitSpan.uninitDone())
569 return;
570}
571
572HRESULT HostUpdate::updateCheck(UpdateCheckType_T aCheckType,
573 ComPtr<IProgress> &aProgress)
574{
575 /* Validate input */
576 switch (aCheckType)
577 {
578 case UpdateCheckType_VirtualBox:
579 break;
580 case UpdateCheckType_ExtensionPack:
581 return setError(E_NOTIMPL, tr("UpdateCheckType::ExtensionPack is not implemented"));
582 case UpdateCheckType_GuestAdditions:
583 return setError(E_NOTIMPL, tr("UpdateCheckType::GuestAdditions is not implemented"));
584 default:
585 return setError(E_INVALIDARG, tr("Invalid aCheckType value %d"), aCheckType);
586 }
587
588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
589
590 // Check whether VirtualBox updates have been disabled before spawning the task thread.
591 ComPtr<ISystemProperties> pSystemProperties;
592 HRESULT rc = mVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
593 if (FAILED(rc))
594 return setErrorVrc(rc, tr("%s: IVirtualBox::systemProperties() failed: %Rrc"), __FUNCTION__, rc);
595
596 BOOL fVBoxUpdateEnabled = true;
597 rc = pSystemProperties->COMGETTER(VBoxUpdateEnabled)(&fVBoxUpdateEnabled);
598 if (FAILED(rc))
599 return setErrorVrc(rc, tr("%s: retrieving ISystemProperties::VBoxUpdateEnabled failed: %Rrc"), __FUNCTION__, rc);
600
601 /** @todo r=bird: Not sure if this makes sense, it should at least have a
602 * better status code and a proper error message. Also, isn't this really
603 * something the caller should check? Presumably the caller already check
604 * whther this was a good time to perform an update check (i.e. the configured
605 * time has elapsed since last check) ...
606 *
607 * It would make sense to allow performing a one-off update check even if the
608 * automatic update checking is disabled, wouldn't it? */
609 if (!fVBoxUpdateEnabled)
610 return E_NOTIMPL;
611
612 ComObjPtr<Progress> pProgress;
613 rc = pProgress.createObject();
614 if (FAILED(rc))
615 return rc;
616
617 rc = pProgress->init(mVirtualBox,
618 static_cast<IHostUpdate*>(this),
619 tr("Checking for software update..."),
620 TRUE /* aCancelable */);
621 if (FAILED(rc))
622 return rc;
623
624 /* initialize the worker task */
625 UpdateCheckTask *pTask = new UpdateCheckTask(aCheckType, this, pProgress);
626 rc = pTask->createThread();
627 pTask = NULL;
628 if (FAILED(rc))
629 return rc;
630
631 rc = pProgress.queryInterfaceTo(aProgress.asOutParam());
632
633 return rc;
634}
635
636HRESULT HostUpdate::getUpdateVersion(com::Utf8Str &aUpdateVersion)
637{
638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
639
640 aUpdateVersion = m_updateVersion;
641
642 return S_OK;
643}
644
645HRESULT HostUpdate::getUpdateURL(com::Utf8Str &aUpdateURL)
646{
647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
648
649 aUpdateURL = m_updateURL;
650
651 return S_OK;
652}
653
654HRESULT HostUpdate::getUpdateResponse(BOOL *aUpdateNeeded)
655{
656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
657
658 *aUpdateNeeded = m_updateNeeded;
659
660 return S_OK;
661}
662
663HRESULT HostUpdate::getUpdateCheckNeeded(BOOL *aUpdateCheckNeeded)
664{
665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
666
667 HRESULT rc;
668 ComPtr<ISystemProperties> pSystemProperties;
669 rc = mVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
670 if (FAILED(rc))
671 return rc;
672
673 /*
674 * Is update checking enabled?
675 */
676 BOOL fVBoxUpdateEnabled;
677 rc = pSystemProperties->COMGETTER(VBoxUpdateEnabled)(&fVBoxUpdateEnabled);
678 if (FAILED(rc))
679 return rc;
680
681 if (!fVBoxUpdateEnabled)
682 {
683 *aUpdateCheckNeeded = false;
684 return S_OK;
685 }
686
687 /*
688 * When was the last update?
689 */
690 Bstr strVBoxUpdateLastCheckDate;
691 rc = pSystemProperties->COMGETTER(VBoxUpdateLastCheckDate)(strVBoxUpdateLastCheckDate.asOutParam());
692 if (FAILED(rc))
693 return rc;
694
695 // No prior update check performed so do so now
696 if (strVBoxUpdateLastCheckDate.isEmpty())
697 {
698 *aUpdateCheckNeeded = true;
699 return S_OK;
700 }
701
702 // convert stored timestamp to time spec
703 RTTIMESPEC LastCheckTime;
704 if (!RTTimeSpecFromString(&LastCheckTime, Utf8Str(strVBoxUpdateLastCheckDate).c_str()))
705 {
706 *aUpdateCheckNeeded = true;
707 return S_OK;
708 }
709
710 /*
711 * Compare last update with how often we are supposed to check for updates.
712 */
713 ULONG uVBoxUpdateFrequency = 0; // value in days
714 rc = pSystemProperties->COMGETTER(VBoxUpdateFrequency)(&uVBoxUpdateFrequency);
715 if (FAILED(rc))
716 return rc;
717
718 if (!uVBoxUpdateFrequency)
719 {
720 /* Consider config (enable, 0 day interval) as checking once but never again.
721 We've already check since we've got a date. */
722 *aUpdateCheckNeeded = false;
723 return S_OK;
724 }
725 uint64_t const cSecsInXDays = uVBoxUpdateFrequency * RT_SEC_1DAY_64;
726
727 RTTIMESPEC TimeDiff;
728 RTTimeSpecSub(RTTimeNow(&TimeDiff), &LastCheckTime);
729
730 LogRelFunc(("Checking if seconds since last check (%lld) >= Number of seconds in %lu day%s (%lld)\n",
731 RTTimeSpecGetSeconds(&TimeDiff), uVBoxUpdateFrequency, uVBoxUpdateFrequency > 1 ? "s" : "", cSecsInXDays));
732
733 if (RTTimeSpecGetSeconds(&TimeDiff) >= (int64_t)cSecsInXDays)
734 *aUpdateCheckNeeded = true;
735
736 return S_OK;
737}
738
739/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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