summaryrefslogtreecommitdiffstats
path: root/src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp')
-rw-r--r--src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp392
1 files changed, 392 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp b/src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp
new file mode 100644
index 00000000..5b66b70b
--- /dev/null
+++ b/src/VBox/Frontends/VBoxManage/VBoxManageUpdateCheck.cpp
@@ -0,0 +1,392 @@
+/* $Id: VBoxManageUpdateCheck.cpp $ */
+/** @file
+ * VBoxManage - The 'updatecheck' command.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/com.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/com/VirtualBox.h>
+#include <VBox/com/array.h>
+
+#include <iprt/buildconfig.h>
+#include <VBox/version.h>
+
+#include <VBox/log.h>
+#include <iprt/getopt.h>
+#include <iprt/stream.h>
+#include <iprt/ctype.h>
+#include <iprt/message.h>
+
+#include "VBoxManage.h"
+
+DECLARE_TRANSLATION_CONTEXT(UpdateCheck);
+
+using namespace com; // SafeArray
+
+
+static RTEXITCODE doUpdateList(int argc, char **argv, ComPtr<IUpdateAgent> pUpdateAgent)
+{
+ /*
+ * Parse options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--machine-readable", 'm', RTGETOPT_REQ_NOTHING }
+ };
+ RTGETOPTSTATE GetState;
+ int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0 /* First */, 0);
+ AssertRCReturn(vrc, RTEXITCODE_INIT);
+
+ bool fMachineReadable = false;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case 'm':
+ fMachineReadable = true;
+ break;
+
+ default:
+ return errorGetOpt(c, &ValueUnion);
+ }
+ }
+
+ /*
+ * Do the work.
+ */
+ BOOL fEnabled;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(Enabled)(&fEnabled), RTEXITCODE_FAILURE);
+ if (fMachineReadable)
+ outputMachineReadableBool("enabled", &fEnabled);
+ else
+ RTPrintf(UpdateCheck::tr("Enabled: %s\n"),
+ fEnabled ? UpdateCheck::tr("yes") : UpdateCheck::tr("no"));
+
+ ULONG cCheckCount;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(CheckCount)(&cCheckCount), RTEXITCODE_FAILURE);
+ if (fMachineReadable)
+ outputMachineReadableULong("count", &cCheckCount);
+ else
+ RTPrintf(UpdateCheck::tr("Count: %u\n"), cCheckCount);
+
+ ULONG uCheckFreqSeconds;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(CheckFrequency)(&uCheckFreqSeconds), RTEXITCODE_FAILURE);
+
+ ULONG uCheckFreqDays = uCheckFreqSeconds / RT_SEC_1DAY;
+
+ if (fMachineReadable)
+ outputMachineReadableULong("frequency-days", &uCheckFreqDays);
+ else if (uCheckFreqDays == 0)
+ RTPrintf(UpdateCheck::tr("Frequency: Never\n"));
+ else if (uCheckFreqDays == 1)
+ RTPrintf(UpdateCheck::tr("Frequency: Every day\n"));
+ else
+ RTPrintf(UpdateCheck::tr("Frequency: Every %u days\n"), uCheckFreqDays);
+
+ UpdateChannel_T enmUpdateChannel;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(Channel)(&enmUpdateChannel), RTEXITCODE_FAILURE);
+ const char *psz;
+ const char *pszMachine;
+ switch (enmUpdateChannel)
+ {
+ case UpdateChannel_Stable:
+ psz = UpdateCheck::tr("Stable - new minor and maintenance releases");
+ pszMachine = "stable";
+ break;
+ case UpdateChannel_All:
+ psz = UpdateCheck::tr("All releases - new minor, maintenance, and major releases");
+ pszMachine = "all-releases";
+ break;
+ case UpdateChannel_WithBetas:
+ psz = UpdateCheck::tr("With Betas - new minor, maintenance, major, and beta releases");
+ pszMachine = "with-betas";
+ break;
+ default:
+ AssertFailed();
+ psz = UpdateCheck::tr("Unset");
+ pszMachine = "invalid";
+ break;
+ }
+ if (fMachineReadable)
+ outputMachineReadableString("channel", pszMachine);
+ else
+ RTPrintf(UpdateCheck::tr("Channel: %s\n"), psz);
+
+ Bstr bstrVal;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(LastCheckDate)(bstrVal.asOutParam()),
+ RTEXITCODE_FAILURE);
+ if (fMachineReadable)
+ outputMachineReadableString("last-check-date", &bstrVal);
+ else if (bstrVal.isNotEmpty())
+ RTPrintf(UpdateCheck::tr("Last Check Date: %ls\n"), bstrVal.raw());
+
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(RepositoryURL)(bstrVal.asOutParam()), RTEXITCODE_FAILURE);
+ if (fMachineReadable)
+ outputMachineReadableString("repo-url", &bstrVal);
+ else
+ RTPrintf(UpdateCheck::tr("Repository: %ls\n"), bstrVal.raw());
+
+ return RTEXITCODE_SUCCESS;
+}
+
+static RTEXITCODE doUpdateModify(int argc, char **argv, ComPtr<IUpdateAgent> pUpdateAgent)
+{
+ /*
+ * Parse options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--enable", 'e', RTGETOPT_REQ_NOTHING },
+ { "--disable", 'd', RTGETOPT_REQ_NOTHING },
+ { "--channel", 'c', RTGETOPT_REQ_STRING },
+ { "--frequency", 'f', RTGETOPT_REQ_UINT32 },
+ };
+
+ RTGETOPTSTATE GetState;
+ int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0 /* First */, 0);
+ AssertRCReturn(vrc, RTEXITCODE_INIT);
+
+ int fEnabled = -1; /* Tristate: -1 (not modified), false, true. */
+ UpdateChannel_T enmChannel = (UpdateChannel_T)-1;
+ uint32_t cFrequencyDays = 0;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case 'e':
+ fEnabled = true;
+ break;
+
+ case 'd':
+ fEnabled = false;
+ break;
+
+ case 'c':
+ if (!RTStrICmp(ValueUnion.psz, "stable"))
+ enmChannel = UpdateChannel_Stable;
+ else if (!RTStrICmp(ValueUnion.psz, "withbetas"))
+ enmChannel = UpdateChannel_WithBetas;
+ /** @todo UpdateChannel_WithTesting once supported. */
+ else if (!RTStrICmp(ValueUnion.psz, "all"))
+ enmChannel = UpdateChannel_All;
+ else
+ return errorArgument(UpdateCheck::tr("Invalid channel specified: '%s'"), ValueUnion.psz);
+ break;
+
+ case 'f':
+ cFrequencyDays = ValueUnion.u32;
+ if (cFrequencyDays == 0)
+ return errorArgument(UpdateCheck::tr("The update frequency cannot be zero"));
+ break;
+
+ /** @todo Add more options like repo handling etc. */
+
+ default:
+ return errorGetOpt(c, &ValueUnion);
+ }
+ }
+
+ if ( fEnabled == -1
+ && enmChannel == (UpdateChannel_T)-1
+ && cFrequencyDays == 0)
+ return errorSyntax(UpdateCheck::tr("No change requested"));
+
+ /*
+ * Make the changes.
+ */
+ if (enmChannel != (UpdateChannel_T)-1)
+ {
+ CHECK_ERROR2I_RET(pUpdateAgent, COMSETTER(Channel)(enmChannel), RTEXITCODE_FAILURE);
+ }
+ if (fEnabled != -1)
+ {
+ CHECK_ERROR2I_RET(pUpdateAgent, COMSETTER(Enabled)((BOOL)fEnabled), RTEXITCODE_FAILURE);
+ }
+ if (cFrequencyDays)
+ {
+ CHECK_ERROR2I_RET(pUpdateAgent, COMSETTER(CheckFrequency)(cFrequencyDays * RT_SEC_1DAY), RTEXITCODE_FAILURE);
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+static RTEXITCODE doUpdateCheck(int argc, char **argv, ComPtr<IUpdateAgent> pUpdateAgent)
+{
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--machine-readable", 'm', RTGETOPT_REQ_NOTHING }
+ };
+ RTGETOPTSTATE GetState;
+ int vrc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0 /* First */, 0);
+ AssertRCReturn(vrc, RTEXITCODE_INIT);
+
+ bool fMachineReadable = false;
+
+ int c;
+ RTGETOPTUNION ValueUnion;
+ while ((c = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (c)
+ {
+ case 'm':
+ fMachineReadable = true;
+ break;
+
+ default:
+ return errorGetOpt(c, &ValueUnion);
+ }
+ }
+
+ /*
+ * Do the work.
+ */
+ Bstr bstrName;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(Name)(bstrName.asOutParam()), RTEXITCODE_FAILURE);
+
+ if (!fMachineReadable)
+ RTPrintf(UpdateCheck::tr("Checking for a new %ls version...\n"), bstrName.raw());
+
+ /*
+ * We don't call CHECK_ERROR2I_RET(pHostUpdate, VBoxUpdate(updateCheckType, ...); here so we can check for a specific
+ * return value indicating update checks are disabled.
+ */
+ ComPtr<IProgress> pProgress;
+ HRESULT hrc = pUpdateAgent->CheckFor(pProgress.asOutParam());
+ if (FAILED(hrc))
+ {
+ if (pProgress.isNull())
+ RTStrmPrintf(g_pStdErr, UpdateCheck::tr("Failed to create update progress object: %Rhrc\n"), hrc);
+ else
+ com::GlueHandleComError(pUpdateAgent, "HostUpdate(UpdateChannel_Stable, pProgress.asOutParam())",
+ hrc, __FILE__, __LINE__);
+ return RTEXITCODE_FAILURE;
+ }
+
+ /* HRESULT hrc = */ showProgress(pProgress, fMachineReadable ? SHOW_PROGRESS_NONE : SHOW_PROGRESS);
+ CHECK_PROGRESS_ERROR_RET(pProgress, (UpdateCheck::tr("Checking for update failed.")), RTEXITCODE_FAILURE);
+
+ UpdateState_T updateState;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(State)(&updateState), RTEXITCODE_FAILURE);
+
+ BOOL const fUpdateNeeded = updateState == UpdateState_Available;
+ if (fMachineReadable)
+ outputMachineReadableBool("update-needed", &fUpdateNeeded);
+
+ switch (updateState)
+ {
+ case UpdateState_Available:
+ {
+ Bstr bstrUpdateVersion;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(Version)(bstrUpdateVersion.asOutParam()), RTEXITCODE_FAILURE);
+ Bstr bstrUpdateURL;
+ CHECK_ERROR2I_RET(pUpdateAgent, COMGETTER(DownloadUrl)(bstrUpdateURL.asOutParam()), RTEXITCODE_FAILURE);
+
+ if (!fMachineReadable)
+ RTPrintf(UpdateCheck::tr(
+ "A new version of %ls has been released! Version %ls is available at virtualbox.org.\n"
+ "You can download this version here: %ls\n"),
+ bstrName.raw(), bstrUpdateVersion.raw(), bstrUpdateURL.raw());
+ else
+ {
+ outputMachineReadableString("update-version", &bstrUpdateVersion);
+ outputMachineReadableString("update-url", &bstrUpdateURL);
+ }
+
+ break;
+ }
+
+ case UpdateState_NotAvailable:
+ {
+ if (!fMachineReadable)
+ RTPrintf(UpdateCheck::tr("You are already running the most recent version of %ls.\n"), bstrName.raw());
+ break;
+ }
+
+ case UpdateState_Canceled:
+ break;
+
+ case UpdateState_Error:
+ RT_FALL_THROUGH();
+ default:
+ {
+ if (!fMachineReadable)
+ RTPrintf(UpdateCheck::tr("Something went wrong while checking for updates!\n"
+ "Please check network connection and try again later.\n"));
+ break;
+ }
+ }
+
+ return RTEXITCODE_SUCCESS;
+}
+
+/**
+ * Handles the 'updatecheck' command.
+ *
+ * @returns Appropriate exit code.
+ * @param a Handler argument.
+ */
+RTEXITCODE handleUpdateCheck(HandlerArg *a)
+{
+ ComPtr<IHost> pHost;
+ CHECK_ERROR2I_RET(a->virtualBox, COMGETTER(Host)(pHost.asOutParam()), RTEXITCODE_FAILURE);
+
+ ComPtr<IUpdateAgent> pUpdate;
+ CHECK_ERROR2I_RET(pHost, COMGETTER(UpdateHost)(pUpdate.asOutParam()), RTEXITCODE_FAILURE);
+ /** @todo Add other update agents here. */
+
+ if (a->argc < 1)
+ return errorNoSubcommand();
+ if (!RTStrICmp(a->argv[0], "perform"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_UPDATECHECK_PERFORM);
+ return doUpdateCheck(a->argc - 1, &a->argv[1], pUpdate);
+ }
+ if (!RTStrICmp(a->argv[0], "list"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_UPDATECHECK_LIST);
+ return doUpdateList(a->argc - 1, &a->argv[1], pUpdate);
+ }
+ if (!RTStrICmp(a->argv[0], "modify"))
+ {
+ setCurrentSubcommand(HELP_SCOPE_UPDATECHECK_MODIFY);
+ return doUpdateModify(a->argc - 1, &a->argv[1], pUpdate);
+ }
+ return errorUnknownSubcommand(a->argv[0]);
+}
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */