diff --git a/DistroLauncher-Tests/DistroLauncher-Tests/DistroLauncher-Tests.vcxproj b/DistroLauncher-Tests/DistroLauncher-Tests/DistroLauncher-Tests.vcxproj index 043db32e..99e7f53b 100644 --- a/DistroLauncher-Tests/DistroLauncher-Tests/DistroLauncher-Tests.vcxproj +++ b/DistroLauncher-Tests/DistroLauncher-Tests/DistroLauncher-Tests.vcxproj @@ -111,6 +111,7 @@ if EXIST "$(SharedIdb)" xcopy /Y /F "$(SharedIdb)" "$(IntDir)" + diff --git a/DistroLauncher-Tests/DistroLauncher-Tests/DistroLauncher-Tests.vcxproj.filters b/DistroLauncher-Tests/DistroLauncher-Tests/DistroLauncher-Tests.vcxproj.filters index 6b835077..64af04d3 100644 --- a/DistroLauncher-Tests/DistroLauncher-Tests/DistroLauncher-Tests.vcxproj.filters +++ b/DistroLauncher-Tests/DistroLauncher-Tests/DistroLauncher-Tests.vcxproj.filters @@ -104,6 +104,7 @@ + @@ -116,4 +117,4 @@ {c6954f59-9f21-4dd8-a33d-693e8797d882} - + \ No newline at end of file diff --git a/DistroLauncher-Tests/DistroLauncher-Tests/upgrade_policy_tests.cpp b/DistroLauncher-Tests/DistroLauncher-Tests/upgrade_policy_tests.cpp index 7c2d9abc..bdaf07a3 100644 --- a/DistroLauncher-Tests/DistroLauncher-Tests/upgrade_policy_tests.cpp +++ b/DistroLauncher-Tests/DistroLauncher-Tests/upgrade_policy_tests.cpp @@ -20,37 +20,37 @@ TEST(UpgradePolicyTests, StringParsing) { - ASSERT_FALSE(internal::starts_with(L"", L"hello")); - ASSERT_FALSE(internal::starts_with(L"he", L"hello")); - ASSERT_TRUE(internal::starts_with(L"hello", L"hello")); - ASSERT_TRUE(internal::starts_with(L"hello, world!", L"hello")); - ASSERT_FALSE(internal::starts_with(L"cheers, world!", L"hello")); - ASSERT_FALSE(internal::starts_with(L"HELLO", L"hello")); + ASSERT_FALSE(starts_with(L"", L"hello")); + ASSERT_FALSE(starts_with(L"he", L"hello")); + ASSERT_TRUE(starts_with(L"hello", L"hello")); + ASSERT_TRUE(starts_with(L"hello, world!", L"hello")); + ASSERT_FALSE(starts_with(L"cheers, world!", L"hello")); + ASSERT_FALSE(starts_with(L"HELLO", L"hello")); - ASSERT_FALSE(internal::ends_with(L"", L"world!")); - ASSERT_FALSE(internal::ends_with(L"d!", L"world!")); - ASSERT_TRUE(internal::ends_with(L"world!", L"world!")); - ASSERT_TRUE(internal::ends_with(L"hello, world!", L"world!")); - ASSERT_FALSE(internal::ends_with(L"hello, world?", L"world!")); - ASSERT_FALSE(internal::ends_with(L"this string is completely diferent", L"world!")); - ASSERT_FALSE(internal::ends_with(L"HELLO", L"hello")); + ASSERT_FALSE(ends_with(L"", L"world!")); + ASSERT_FALSE(ends_with(L"d!", L"world!")); + ASSERT_TRUE(ends_with(L"world!", L"world!")); + ASSERT_TRUE(ends_with(L"hello, world!", L"world!")); + ASSERT_FALSE(ends_with(L"hello, world?", L"world!")); + ASSERT_FALSE(ends_with(L"this string is completely diferent", L"world!")); + ASSERT_FALSE(ends_with(L"HELLO", L"hello")); - ASSERT_FALSE(internal::starts_with(L"", L"Ubuntu")); - ASSERT_TRUE(internal::starts_with(L"Ubuntu 22.04.1 LTS", L"Ubuntu")); - ASSERT_TRUE(internal::ends_with(L"Ubuntu 22.04.1 LTS", L"LTS")); + ASSERT_FALSE(starts_with(L"", L"Ubuntu")); + ASSERT_TRUE(starts_with(L"Ubuntu 22.04.1 LTS", L"Ubuntu")); + ASSERT_TRUE(ends_with(L"Ubuntu 22.04.1 LTS", L"LTS")); } TEST(UpgradePolicyTests, Concat) { // Checking default functionality std::wstring dog = L"dog"; - auto example = internal::concat(L"The", L" quick brown fox", L" jumps over the lazy ", std::quoted(dog), '.'); + auto example = concat(L"The", L" quick brown fox", L" jumps over the lazy ", std::quoted(dog), '.'); ASSERT_EQ(example, LR"(The quick brown fox jumps over the lazy "dog".)"); // Checking quote nesting - auto nested = internal::concat(L"\n{\n ", std::quoted(L"name"), L": ", std::quoted(L"example"), L",\n ", - std::quoted(L"value"), L": ", std::quoted(example), L"\n}\n"); + auto nested = concat(L"\n{\n ", std::quoted(L"name"), L": ", std::quoted(L"example"), L",\n ", + std::quoted(L"value"), L": ", std::quoted(example), L"\n}\n"); auto expected = LR"( { "name": "example", @@ -61,7 +61,6 @@ TEST(UpgradePolicyTests, Concat) // Checking path auto-quoting std::filesystem::path example_file{L"/home/fox/documents/example.json"}; - auto with_path = - internal::concat(L"diff ", example_file, L" ", example_file.wstring()); // Only first one to be quoted + auto with_path = concat(L"diff ", example_file, L" ", example_file.wstring()); // Only first one to be quoted ASSERT_EQ(with_path, LR"(diff "/home/fox/documents/example.json" /home/fox/documents/example.json)"); } diff --git a/DistroLauncher/DistroLauncher.vcxproj b/DistroLauncher/DistroLauncher.vcxproj index fd5bb906..fd57d4d7 100644 --- a/DistroLauncher/DistroLauncher.vcxproj +++ b/DistroLauncher/DistroLauncher.vcxproj @@ -138,6 +138,7 @@ + diff --git a/DistroLauncher/WSLInfo.cpp b/DistroLauncher/WSLInfo.cpp index b624c98b..8a273e91 100644 --- a/DistroLauncher/WSLInfo.cpp +++ b/DistroLauncher/WSLInfo.cpp @@ -137,6 +137,28 @@ namespace Oobe::internal return ini_find_value(wslConf, L"boot", L"command", L"/usr/libexec/wsl-systemd"); } + bool hasSnap(std::wstring_view name) + { + namespace fs = std::filesystem; + const auto path = Oobe::WindowsPath(fs::path{L"/var/lib/snapd/snaps/"}); + return find_file_if(path, [name](const auto& entry) { + return fs::is_regular_file(entry) && starts_with({entry.path().filename().wstring()}, name); + }); + } + + bool hasUdiSnap() + { + static bool hasUdi = hasSnap(L"ubuntu-desktop-installer"); + + return hasUdi; + } + + bool hasSubiquitySnap() + { + static bool hasSubiquity = hasSnap(L"subiquity"); + return hasSubiquity; + } + } // namespace Oobe::internal // This was kept under Helpers namespace to avoid touching OOBE.cpp/h files. @@ -145,8 +167,17 @@ namespace Helpers { bool WslGraphicsSupported() { + // It's possible that we have only Subiquity instead of the Ubuntu-Desktop-Installer snap. + if (!Oobe::internal::hasUdiSnap()) { + return false; + } + + if (!Oobe::internal::isWslgEnabled()) { + return false; + } + // Could WSL 3 or greater exist in the future? - return (Oobe::internal::isWslgEnabled() && Oobe::internal::WslGetDistroSubsystemVersion() > 1); + return Oobe::internal::WslGetDistroSubsystemVersion() > 1; } } // namespace Helpers. diff --git a/DistroLauncher/WSLInfo.h b/DistroLauncher/WSLInfo.h index b3d5cbd1..44b29cf1 100644 --- a/DistroLauncher/WSLInfo.h +++ b/DistroLauncher/WSLInfo.h @@ -49,6 +49,15 @@ namespace Oobe::internal /// bool isLocalhostForwardingEnabled(const std::filesystem::path& wslConfig); + /// Returns true if the ubuntu-desktop-installer snap is found inside the rootfs. + /// It's possible that we have only Subiquity or none instead. + /// That's meant to be called during setup, where filesystem errors are less likely to happen. + bool hasUdiSnap(); + + /// Returns true if the subiquity snap is found inside the rootfs. + /// It's possible that we have the ubuntu-desktop-installer or none instead. + /// That's meant to be called during setup, where filesystem errors are less likely to happen. + bool hasSubiquitySnap(); } namespace Oobe diff --git a/DistroLauncher/algorithms.h b/DistroLauncher/algorithms.h new file mode 100644 index 00000000..f7302af1 --- /dev/null +++ b/DistroLauncher/algorithms.h @@ -0,0 +1,70 @@ +#pragma once +/* + * Copyright (C) 2022 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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 . + * + */ + +// Common algorithms to be used everywhere in the launcher project with style resembling the std ones. + +template +bool starts_with(const std::basic_string_view tested, const std::basic_string_view start) +{ + if (tested.size() < start.size()) { + return false; + } + auto mismatch = std::mismatch(start.cbegin(), start.cend(), tested.cbegin()); + return mismatch.first == start.cend(); +} + +template +bool starts_with(CharT const (&tested)[TestedSize], CharT const (&start)[StartSize]) +{ + return starts_with(std::basic_string_view{tested}, std::basic_string_view{start}); +} + +template +bool ends_with(const std::basic_string_view tested, const std::basic_string_view end) +{ + if (tested.size() < end.size()) { + return false; + } + auto mismatch = std::mismatch(end.crbegin(), end.crend(), tested.crbegin()); + return mismatch.first == end.crend(); +} + +template +bool ends_with(const CharT (&tested)[TestedSize], const CharT (&end)[EndSize]) +{ + return ends_with(std::basic_string_view{tested}, std::basic_string_view{end}); +} + +template std::wstring concat(Args&&... args) +{ + std::wstringstream buffer; + (buffer << ... << std::forward(args)); + return buffer.str(); +} + +template bool find_file_if(const std::filesystem::path& directory, Pred&& pred) +{ + namespace fs = std::filesystem; + std::error_code error; + assert(fs::is_directory(directory)); + auto listing = fs::directory_iterator{directory, error}; + if (error) { + return false; + } + return std::find_if(begin(listing), end(listing), std::forward(pred)) != end(listing); +} diff --git a/DistroLauncher/stdafx.h b/DistroLauncher/stdafx.h index 825bc8f1..836f80cd 100644 --- a/DistroLauncher/stdafx.h +++ b/DistroLauncher/stdafx.h @@ -41,6 +41,7 @@ #include #include #include +#include "algorithms.h" #include "expected.hpp" #include "not_null.h" #include "WslApiLoader.h" diff --git a/DistroLauncher/upgrade_policy.cpp b/DistroLauncher/upgrade_policy.cpp index 118ed70d..bb892217 100644 --- a/DistroLauncher/upgrade_policy.cpp +++ b/DistroLauncher/upgrade_policy.cpp @@ -20,31 +20,13 @@ namespace internal { - bool starts_with(const std::wstring_view tested, const std::wstring_view start) - { - if (tested.size() < start.size()) { - return false; - } - auto mismatch = std::mismatch(start.cbegin(), start.cend(), tested.cbegin()); - return mismatch.first == start.cend(); - } - - bool ends_with(const std::wstring_view tested, const std::wstring_view end) - { - if (tested.size() < end.size()) { - return false; - } - auto mismatch = std::mismatch(end.crbegin(), end.crend(), tested.crbegin()); - return mismatch.first == end.crend(); - } - std::wstring GetUpgradePolicy() { std::wstring_view name = DistributionInfo::Name; if (name == L"Ubuntu") { return L"lts"; } - if (starts_with(name, L"Ubuntu") && ends_with(name, L"LTS")) { + if (starts_with(name, {L"Ubuntu"}) && ends_with(name, {L"LTS"})) { return L"never"; } return L"normal"; diff --git a/DistroLauncher/upgrade_policy.h b/DistroLauncher/upgrade_policy.h index e6eea1f1..6cc24289 100644 --- a/DistroLauncher/upgrade_policy.h +++ b/DistroLauncher/upgrade_policy.h @@ -19,18 +19,8 @@ namespace internal { - bool starts_with(std::wstring_view tested, std::wstring_view start); - bool ends_with(std::wstring_view tested, std::wstring_view end); - std::wstring GetUpgradePolicy(); - template std::wstring concat(Args&&... args) - { - std::wstringstream buffer; - (buffer << ... << std::forward(args)); - return buffer.str(); - } - void SetDefaultUpgradePolicyImpl(); }