VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.2 KB
Line 
1/* $Id: HostUpdateImpl.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IHostUpdate COM class implementations.
4 */
5
6/*
7 * Copyright (C) 2020-2022 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 // Default to no update required
260 m_updateNeeded = FALSE;
261
262 // Following the sequence of steps in UIUpdateStepVirtualBox::sltStartStep()
263 // Build up our query URL starting with the URL basename
264 Utf8Str strUrl("https://update.virtualbox.org/query.php/?");
265 Bstr platform;
266 HRESULT rc = mVirtualBox->COMGETTER(PackageType)(platform.asOutParam());
267 AssertComRCReturn(rc, rc);
268 strUrl.appendPrintf("platform=%ls", platform.raw()); // e.g. SOLARIS_64BITS_GENERIC
269
270 // Get the complete current version string for the query URL
271 Bstr versionNormalized;
272 rc = mVirtualBox->COMGETTER(VersionNormalized)(versionNormalized.asOutParam());
273 AssertComRCReturn(rc, rc);
274 strUrl.appendPrintf("&version=%ls", versionNormalized.raw()); // e.g. 6.1.1
275 // strUrl.appendPrintf("&version=6.0.12"); // comment out previous line and uncomment this one for testing
276
277 ULONG revision = 0;
278 rc = mVirtualBox->COMGETTER(Revision)(&revision);
279 AssertComRCReturn(rc, rc);
280 strUrl.appendPrintf("_%u", revision); // e.g. 135618
281
282 // acquire the System Properties interface
283 ComPtr<ISystemProperties> ptrSystemProperties;
284 rc = mVirtualBox->COMGETTER(SystemProperties)(ptrSystemProperties.asOutParam());
285 AssertComRCReturn(rc, rc);
286
287 // Update the VBoxUpdate setting 'VBoxUpdateLastCheckDate'
288 RTTIME Time;
289 RTTIMESPEC TimeNow;
290 char szTimeStr[RTTIME_STR_LEN];
291 RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&TimeNow)), szTimeStr, sizeof(szTimeStr));
292 LogRelFunc(("VBox updating UpdateDate with TimeString = %s\n", szTimeStr));
293 rc = ptrSystemProperties->COMSETTER(VBoxUpdateLastCheckDate)(Bstr(szTimeStr).raw());
294 AssertComRCReturn(rc, rc);
295
296 // Update the queryURL and the VBoxUpdate setting 'VBoxUpdateCount'
297 ULONG cVBoxUpdateCount = 0;
298 rc = ptrSystemProperties->COMGETTER(VBoxUpdateCount)(&cVBoxUpdateCount);
299 AssertComRCReturn(rc, rc);
300
301 cVBoxUpdateCount++;
302
303 rc = ptrSystemProperties->COMSETTER(VBoxUpdateCount)(cVBoxUpdateCount);
304 AssertComRCReturn(rc, rc);
305 strUrl.appendPrintf("&count=%u", cVBoxUpdateCount);
306
307 // Update the query URL and the VBoxUpdate settings (if necessary) with the 'Target' information.
308 VBoxUpdateTarget_T enmTarget = VBoxUpdateTarget_Stable; // default branch is 'stable'
309 rc = ptrSystemProperties->COMGETTER(VBoxUpdateTarget)(&enmTarget);
310 AssertComRCReturn(rc, rc);
311
312 switch (enmTarget)
313 {
314 case VBoxUpdateTarget_AllReleases:
315 strUrl.appendPrintf("&branch=allrelease"); // query.php expects 'allrelease' and not 'allreleases'
316 break;
317 case VBoxUpdateTarget_WithBetas:
318 strUrl.appendPrintf("&branch=withbetas");
319 break;
320 case VBoxUpdateTarget_Stable:
321 default:
322 strUrl.appendPrintf("&branch=stable");
323 break;
324 }
325
326 rc = ptrSystemProperties->COMSETTER(VBoxUpdateTarget)(enmTarget);
327 AssertComRCReturn(rc, rc);
328
329 LogRelFunc(("VBox update URL = %s\n", strUrl.c_str()));
330
331 /*
332 * Compose the User-Agent header for the GET request.
333 */
334 Bstr version;
335 rc = mVirtualBox->COMGETTER(Version)(version.asOutParam()); // e.g. 6.1.0_RC1
336 AssertComRCReturn(rc, rc);
337
338 Utf8StrFmt const strUserAgent("VirtualBox %ls <%s>", version.raw(), HostUpdate::i_platformInfo().c_str());
339 LogRelFunc(("userAgent = %s\n", strUserAgent.c_str()));
340
341 /*
342 * Create the HTTP client instance and pass it to a inner worker method to
343 * ensure proper cleanup.
344 */
345 RTHTTP hHttp = NIL_RTHTTP;
346 int vrc = RTHttpCreate(&hHttp);
347 if (RT_SUCCESS(vrc))
348 {
349 try
350 {
351 rc = i_checkForVBoxUpdateInner(hHttp, strUrl, strUserAgent, ptrSystemProperties);
352 }
353 catch (...)
354 {
355 AssertFailed();
356 rc = E_UNEXPECTED;
357 }
358 RTHttpDestroy(hHttp);
359 }
360 else
361 rc = setErrorVrc(vrc, tr("%s: RTHttpCreate() failed: %Rrc"), __FUNCTION__, vrc);
362 return S_OK;
363}
364
365HRESULT HostUpdate::i_checkForVBoxUpdateInner(RTHTTP hHttp, Utf8Str const &strUrl, Utf8Str const &strUserAgent,
366 ComPtr<ISystemProperties> const &ptrSystemProperties)
367{
368 /// @todo Are there any other headers needed to be added first via RTHttpSetHeaders()?
369 int vrc = RTHttpAddHeader(hHttp, "User-Agent", strUserAgent.c_str(), strUserAgent.length(), RTHTTPADDHDR_F_BACK);
370 if (RT_FAILURE(vrc))
371 return setErrorVrc(vrc, tr("%s: RTHttpAddHeader() failed: %Rrc (on User-Agent)"), __FUNCTION__, vrc);
372
373 /*
374 * Configure proxying.
375 */
376 ProxyMode_T enmProxyMode;
377 HRESULT rc = ptrSystemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
378 if (FAILED(rc))
379 return setError(rc, tr("%s: ISystemProperties::proxyMode() failed: %Rrc"), __FUNCTION__, rc);
380
381 if (enmProxyMode == ProxyMode_Manual)
382 {
383 Bstr strProxyURL;
384 rc = ptrSystemProperties->COMGETTER(ProxyURL)(strProxyURL.asOutParam());
385 if (FAILED(rc))
386 return setError(rc, tr("%s: ISystemProperties::proxyURL() failed: %Rrc"), __FUNCTION__, rc);
387 vrc = RTHttpSetProxyByUrl(hHttp, Utf8Str(strProxyURL).c_str());
388 if (RT_FAILURE(vrc))
389 return setErrorVrc(vrc, tr("%s: RTHttpSetProxyByUrl() failed: %Rrc"), __FUNCTION__, vrc);
390 }
391 else if (enmProxyMode == ProxyMode_System)
392 {
393 vrc = RTHttpUseSystemProxySettings(hHttp);
394 if (RT_FAILURE(vrc))
395 return setErrorVrc(vrc, tr("%s: RTHttpUseSystemProxySettings() failed: %Rrc"), __FUNCTION__, vrc);
396 }
397 else
398 Assert(enmProxyMode == ProxyMode_NoProxy);
399
400 /*
401 * Perform the GET request, returning raw binary stuff.
402 */
403 void *pvResponse = NULL;
404 size_t cbResponse = 0;
405 vrc = RTHttpGetBinary(hHttp, strUrl.c_str(), &pvResponse, &cbResponse);
406 if (RT_FAILURE(vrc))
407 return setErrorVrc(vrc, tr("%s: RTHttpGetBinary() failed: %Rrc"), __FUNCTION__, vrc);
408
409 /* Note! We can do nothing that might throw exceptions till we call RTHttpFreeResponse! */
410
411 /*
412 * If url is platform=DARWIN_64BITS_GENERIC&version=6.0.12&branch=stable for example, the reply is:
413 * 6.0.14<SPACE>https://download.virtualbox.org/virtualbox/6.0.14/VirtualBox-6.0.14-133895-OSX.dmg
414 * If no update required, 'UPTODATE' is returned.
415 */
416 /* Parse out the two first words of the response, ignoring whatever follows: */
417 const char *pchResponse = (const char *)pvResponse;
418 while (cbResponse > 0 && *pchResponse == ' ')
419 cbResponse--, pchResponse++;
420
421 char ch;
422 const char *pchWord0 = pchResponse;
423 while (cbResponse > 0 && (ch = *pchResponse) != ' ' && ch != '\0')
424 cbResponse--, pchResponse++;
425 size_t const cchWord0 = (size_t)(pchResponse - pchWord0);
426
427 while (cbResponse > 0 && *pchResponse == ' ')
428 cbResponse--, pchResponse++;
429 const char *pchWord1 = pchResponse;
430 while (cbResponse > 0 && (ch = *pchResponse) != ' ' && ch != '\0')
431 cbResponse--, pchResponse++;
432 size_t const cchWord1 = (size_t)(pchResponse - pchWord1);
433
434 /* Decode the two word: */
435 static char const s_szUpToDate[] = "UPTODATE";
436 if ( cchWord0 == sizeof(s_szUpToDate) - 1
437 && memcmp(pchWord0, s_szUpToDate, sizeof(s_szUpToDate) - 1) == 0)
438 {
439 m_updateNeeded = FALSE;
440 rc = S_OK;
441 }
442 else
443 {
444 vrc = RTStrValidateEncodingEx(pchWord0, cchWord0, 0 /*fFlags*/);
445 if (RT_SUCCESS(vrc))
446 vrc = RTStrValidateEncodingEx(pchWord1, cchWord1, 0 /*fFlags*/);
447 if (RT_SUCCESS(vrc))
448 {
449 /** @todo Any additional sanity checks we could perform here? */
450 rc = m_updateVersion.assignEx(pchWord0, cchWord0);
451 if (SUCCEEDED(rc))
452 {
453 rc = m_updateVersion.assignEx(pchWord1, cchWord1);
454 if (SUCCEEDED(rc))
455 m_updateNeeded = TRUE;
456 }
457 LogRelFunc(("HTTP server reply = %.*s %.*s\n", cchWord0, pchWord0, cchWord1, pchWord1));
458 }
459 else
460 rc = setErrorVrc(vrc, tr("Invalid server response: %Rrc (%.*Rhxs -- %.*Rhxs)"),
461 vrc, cchWord0, pchWord0, cchWord1, pchWord1);
462 }
463
464 RTHttpFreeResponse(pvResponse);
465
466 return rc;
467}
468
469HRESULT HostUpdate::i_updateCheckTask(UpdateCheckTask *pTask)
470{
471 LogFlowFuncEnter();
472 AutoCaller autoCaller(this);
473 HRESULT hrc = autoCaller.rc();
474 if (SUCCEEDED(hrc))
475 {
476 try
477 {
478 switch (pTask->m_checkType)
479 {
480 case UpdateCheckType_VirtualBox:
481 hrc = i_checkForVBoxUpdate();
482 break;
483# if 0
484 case UpdateCheckType_ExtensionPack:
485 hrc = i_checkForExtPackUpdate();
486 break;
487
488 case UpdateCheckType_GuestAdditions:
489 hrc = i_checkForGuestAdditionsUpdate();
490 break;
491# endif
492 default:
493 hrc = setError(E_FAIL, tr("Update check type %d is not implemented"), pTask->m_checkType);
494 break;
495 }
496 }
497 catch (...)
498 {
499 AssertFailed();
500 hrc = E_UNEXPECTED;
501 }
502 }
503
504 if (!pTask->m_ptrProgress.isNull())
505 pTask->m_ptrProgress->i_notifyComplete(hrc);
506
507 LogFlowFunc(("rc=%Rhrc\n", hrc));
508 LogFlowFuncLeave();
509 return hrc;
510}
511
512#endif /* VBOX_WITH_HOST_UPDATE_CHECK */
513
514
515////////////////////////////////////////////////////////////////////////////////
516//
517// HostUpdate constructor / destructor
518//
519// ////////////////////////////////////////////////////////////////////////////////
520HostUpdate::HostUpdate()
521 : mVirtualBox(NULL)
522{
523}
524
525HostUpdate::~HostUpdate()
526{
527}
528
529
530HRESULT HostUpdate::FinalConstruct()
531{
532 return BaseFinalConstruct();
533}
534
535void HostUpdate::FinalRelease()
536{
537 uninit();
538
539 BaseFinalRelease();
540}
541
542HRESULT HostUpdate::init(VirtualBox *aVirtualBox)
543{
544 // Enclose the state transition NotReady->InInit->Ready.
545 AutoInitSpan autoInitSpan(this);
546 AssertReturn(autoInitSpan.isOk(), E_FAIL);
547
548 /* Weak reference to a VirtualBox object */
549 unconst(mVirtualBox) = aVirtualBox;
550
551 autoInitSpan.setSucceeded();
552 return S_OK;
553}
554
555void HostUpdate::uninit()
556{
557 // Enclose the state transition Ready->InUninit->NotReady.
558 AutoUninitSpan autoUninitSpan(this);
559 if (autoUninitSpan.uninitDone())
560 return;
561}
562
563HRESULT HostUpdate::updateCheck(UpdateCheckType_T aCheckType,
564 ComPtr<IProgress> &aProgress)
565{
566#ifdef VBOX_WITH_HOST_UPDATE_CHECK
567 /* Validate input */
568 switch (aCheckType)
569 {
570 case UpdateCheckType_VirtualBox:
571 break;
572 case UpdateCheckType_ExtensionPack:
573 return setError(E_NOTIMPL, tr("UpdateCheckType::ExtensionPack is not implemented"));
574 case UpdateCheckType_GuestAdditions:
575 return setError(E_NOTIMPL, tr("UpdateCheckType::GuestAdditions is not implemented"));
576 default:
577 return setError(E_INVALIDARG, tr("Invalid aCheckType value %d"), aCheckType);
578 }
579
580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
581
582 // Check whether VirtualBox updates have been disabled before spawning the task thread.
583 ComPtr<ISystemProperties> pSystemProperties;
584 HRESULT rc = mVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
585 AssertComRCReturn(rc, rc);
586
587 BOOL fVBoxUpdateEnabled = true;
588 rc = pSystemProperties->COMGETTER(VBoxUpdateEnabled)(&fVBoxUpdateEnabled);
589 AssertComRCReturn(rc, rc);
590
591 /** @todo r=bird: Not sure if this makes sense, it should at least have a
592 * better status code and a proper error message. Also, isn't this really
593 * something the caller should check? Presumably the caller already check
594 * whther this was a good time to perform an update check (i.e. the configured
595 * time has elapsed since last check) ...
596 *
597 * It would make sense to allow performing a one-off update check even if the
598 * automatic update checking is disabled, wouldn't it? */
599 if (!fVBoxUpdateEnabled)
600 return E_NOTIMPL;
601
602 ComObjPtr<Progress> pProgress;
603 rc = pProgress.createObject();
604 if (FAILED(rc))
605 return rc;
606
607 rc = pProgress->init(mVirtualBox,
608 static_cast<IHostUpdate*>(this),
609 tr("Checking for software update..."),
610 TRUE /* aCancelable */);
611 if (FAILED(rc))
612 return rc;
613
614 /* initialize the worker task */
615 UpdateCheckTask *pTask = new UpdateCheckTask(aCheckType, this, pProgress);
616 rc = pTask->createThread();
617 pTask = NULL;
618 if (FAILED(rc))
619 return rc;
620
621 rc = pProgress.queryInterfaceTo(aProgress.asOutParam());
622
623 return rc;
624#else /* !VBOX_WITH_HOST_UPDATE_CHECK */
625 RT_NOREF(aCheckType, aProgress);
626 return setError(E_NOTIMPL, tr("Update checking support was not compiled into this VirtualBox build"));
627#endif /* !VBOX_WITH_HOST_UPDATE_CHECK */
628}
629
630HRESULT HostUpdate::getUpdateVersion(com::Utf8Str &aUpdateVersion)
631{
632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
633
634 aUpdateVersion = m_updateVersion;
635
636 return S_OK;
637}
638
639HRESULT HostUpdate::getUpdateURL(com::Utf8Str &aUpdateURL)
640{
641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
642
643 aUpdateURL = m_updateURL;
644
645 return S_OK;
646}
647
648HRESULT HostUpdate::getUpdateResponse(BOOL *aUpdateNeeded)
649{
650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
651
652 *aUpdateNeeded = m_updateNeeded;
653
654 return S_OK;
655}
656
657HRESULT HostUpdate::getUpdateCheckNeeded(BOOL *aUpdateCheckNeeded)
658{
659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
660
661 HRESULT rc;
662 ComPtr<ISystemProperties> pSystemProperties;
663 rc = mVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
664 if (FAILED(rc))
665 return rc;
666
667 /*
668 * Is update checking enabled?
669 */
670 BOOL fVBoxUpdateEnabled;
671 rc = pSystemProperties->COMGETTER(VBoxUpdateEnabled)(&fVBoxUpdateEnabled);
672 if (FAILED(rc))
673 return rc;
674
675 if (!fVBoxUpdateEnabled)
676 {
677 *aUpdateCheckNeeded = false;
678 return S_OK;
679 }
680
681 /*
682 * When was the last update?
683 */
684 Bstr strVBoxUpdateLastCheckDate;
685 rc = pSystemProperties->COMGETTER(VBoxUpdateLastCheckDate)(strVBoxUpdateLastCheckDate.asOutParam());
686 if (FAILED(rc))
687 return rc;
688
689 // No prior update check performed so do so now
690 if (strVBoxUpdateLastCheckDate.isEmpty())
691 {
692 *aUpdateCheckNeeded = true;
693 return S_OK;
694 }
695
696 // convert stored timestamp to time spec
697 RTTIMESPEC LastCheckTime;
698 if (!RTTimeSpecFromString(&LastCheckTime, Utf8Str(strVBoxUpdateLastCheckDate).c_str()))
699 {
700 *aUpdateCheckNeeded = true;
701 return S_OK;
702 }
703
704 /*
705 * Compare last update with how often we are supposed to check for updates.
706 */
707 ULONG uVBoxUpdateFrequency = 0; // value in days
708 rc = pSystemProperties->COMGETTER(VBoxUpdateFrequency)(&uVBoxUpdateFrequency);
709 if (FAILED(rc))
710 return rc;
711
712 if (!uVBoxUpdateFrequency)
713 {
714 /* Consider config (enable, 0 day interval) as checking once but never again.
715 We've already check since we've got a date. */
716 *aUpdateCheckNeeded = false;
717 return S_OK;
718 }
719 uint64_t const cSecsInXDays = uVBoxUpdateFrequency * RT_SEC_1DAY_64;
720
721 RTTIMESPEC TimeDiff;
722 RTTimeSpecSub(RTTimeNow(&TimeDiff), &LastCheckTime);
723
724 LogRelFunc(("Checking if seconds since last check (%lld) >= Number of seconds in %lu day%s (%lld)\n",
725 RTTimeSpecGetSeconds(&TimeDiff), uVBoxUpdateFrequency, uVBoxUpdateFrequency > 1 ? "s" : "", cSecsInXDays));
726
727 if (RTTimeSpecGetSeconds(&TimeDiff) >= (int64_t)cSecsInXDays)
728 *aUpdateCheckNeeded = true;
729
730 return S_OK;
731}
732
733/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette