VirtualBox

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

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

Main/HostUpdateImpl.cpp: Only enable the core code when we build with libcurl (VBOX_WITH_HOST_UPDATE_CHECK) as not all OEMs includes cURL. [build fix] bugref:7983

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