common/platform_win.cc (122 lines of code) (raw):
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "absl/cleanup/cleanup.h"
#include "absl/log/absl_check.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "common/platform.h"
#include "common/source_location.h"
#include "common/status_macros.h"
namespace cloud_kms {
namespace {
struct SystemVersionInfo {
std::string product_name;
DWORD major_version;
DWORD minor_version;
std::string release_id;
std::string current_build;
};
// Retrieves system version information from the Windows registry.
//
// Using the Win32 API to look up version information would be nicer, but the
// Win32 APIs purposely serve up the wrong information, in an attempt to keep
// poorly coded applcations compatible.
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexa
absl::StatusOr<SystemVersionInfo> GetSystemVersionInfo() {
constexpr LPCSTR kKey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\";
HKEY key_handle;
LSTATUS open_status =
RegOpenKeyEx(HKEY_LOCAL_MACHINE, kKey, 0, KEY_READ, &key_handle);
if (open_status != ERROR_SUCCESS) {
return absl::InternalError(
absl::StrFormat("at %s: error %d opening key '%s'.",
SOURCE_LOCATION.ToString(), open_status, kKey));
}
absl::Cleanup c = [&] {
ABSL_DCHECK_EQ(RegCloseKey(key_handle), ERROR_SUCCESS);
};
auto read_string =
[&](const LPCSTR value_name) -> absl::StatusOr<std::string> {
DWORD length;
LSTATUS query_status = RegQueryValueEx(key_handle, value_name, nullptr,
nullptr, nullptr, &length);
if (query_status != ERROR_SUCCESS) {
return absl::InternalError(absl::StrFormat(
"at %s: error %d retrieving length for '%s'.",
SOURCE_LOCATION.ToString(), query_status, value_name));
}
DWORD type = REG_SZ;
std::string result(length, 0);
query_status =
RegQueryValueEx(key_handle, value_name, nullptr, &type,
reinterpret_cast<LPBYTE>(result.data()), &length);
if (query_status != ERROR_SUCCESS) {
return absl::InternalError(absl::StrFormat(
"at %s: error %d retrieving value for '%s'.",
SOURCE_LOCATION.ToString(), query_status, value_name));
}
return result;
};
auto read_dword = [&](const LPCSTR value_name) -> absl::StatusOr<DWORD> {
DWORD type = REG_DWORD;
DWORD value;
DWORD value_size = sizeof(value);
LSTATUS query_status =
RegQueryValueEx(key_handle, value_name, nullptr, &type,
reinterpret_cast<LPBYTE>(&value), &value_size);
if (query_status != ERROR_SUCCESS) {
return absl::InternalError(absl::StrFormat(
"at %s: error %d retrieving value for '%s'.",
SOURCE_LOCATION.ToString(), query_status, value_name));
}
return value;
};
SystemVersionInfo result;
ASSIGN_OR_RETURN(result.product_name, read_string("ProductName"));
ASSIGN_OR_RETURN(result.major_version,
read_dword("CurrentMajorVersionNumber"));
ASSIGN_OR_RETURN(result.minor_version,
read_dword("CurrentMinorVersionNumber"));
ASSIGN_OR_RETURN(result.release_id, read_string("ReleaseId"));
ASSIGN_OR_RETURN(result.current_build, read_string("CurrentBuild"));
return result;
}
} // namespace
absl::Status EnsureWriteProtected(const char* filename) {
return absl::OkStatus();
}
std::string_view GetTargetPlatform() {
#if _WIN64
return "amd64";
#elif _WIN32
return "x86";
#else
static_assert(false, "unhandled processor type");
#endif
}
std::string GetHostPlatformInfo() {
SYSTEM_INFO arch_info;
GetNativeSystemInfo(&arch_info);
std::string arch;
switch (arch_info.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_AMD64:
arch = "amd64";
break;
case PROCESSOR_ARCHITECTURE_ARM:
arch = "arm";
break;
case PROCESSOR_ARCHITECTURE_ARM64:
arch = "aarch64";
break;
case PROCESSOR_ARCHITECTURE_INTEL:
arch = "x86";
break;
default:
arch = "unknown";
break;
}
absl::StatusOr<SystemVersionInfo> info = GetSystemVersionInfo();
if (!info.ok()) {
return absl::StrCat("Windows/unknown-", arch);
}
return absl::StrFormat("%s/%d.%d.%s.%s-%s", info->product_name,
info->major_version, info->minor_version,
info->release_id, info->current_build, arch);
}
void WriteToSystemLog(const char* message) {
// Maybe this could be implemented but it's pretty gross.
// Windows event logs are highly structured and require a registered source.
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-reporteventa
// https://learn.microsoft.com/en-us/windows/win32/eventlog/event-sources
}
} // namespace cloud_kms