You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

367 lines
14 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2023 Intel Corporation. All Rights Reserved.
#include "metadata-helper.h"
#ifdef WIN32
#include <Windows.h>
#include <memory>
#include <vector>
#include <algorithm>
#include <iterator>
#include <regex>
#include <iostream>
#include <sstream>
#include <functional>
#include <map>
#include <memory>
#include <librealsense2/rs.hpp>
#include <rsutils/string/windows.h>
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
#ifdef _MSC_VER
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif
#endif
namespace rs2
{
#ifdef WIN32
struct device_id
{
std::string pid, mi, guid, sn;
};
inline bool equal(const std::string& a, const std::string& b)
{
return strcasecmp(a.c_str(), b.c_str()) == 0;
}
inline bool operator==(const device_id& a, const device_id& b)
{
return equal(a.pid, b.pid) &&
equal(a.guid, b.guid) &&
equal(a.mi, b.mi) &&
equal(a.sn, b.sn);
}
class windows_metadata_helper : public metadata_helper
{
using super = metadata_helper;
public:
static bool parse_device_id(const std::string& id, device_id* res)
{
static const std::regex regex("pid_([0-9a-f]+)&mi_([0-9a-f]+)#[0-9a-f]&([0-9a-f]+)&[\\s\\S]*\\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\}", std::regex_constants::icase);
std::match_results<std::string::const_iterator> match;
if (std::regex_search(id, match, regex) && match.size() > 4)
{
res->pid = match[1];
res->mi = match[2];
res->sn = match[3];
res->guid = match[4];
return true;
}
return false;
}
static void foreach_device_path(const std::vector<device_id>& devices,
std::function<void(const device_id&, /* matched device */
std::wstring /* registry key of Device Parameters for that device */)> action)
{
std::map<std::string, std::vector<device_id>> guid_to_devices;
for (auto&& d : devices)
{
guid_to_devices[d.guid].push_back(d);
}
for (auto&& kvp : guid_to_devices)
{
auto guid = kvp.first;
std::stringstream ss;
ss << "SYSTEM\\CurrentControlSet\\Control\\DeviceClasses\\{" << guid << "}";
auto s = ss.str();
std::wstring prefix(s.begin(), s.end());
HKEY key;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, prefix.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &key) == ERROR_SUCCESS)
{
// Don't forget to release in the end:
std::shared_ptr<void> raii(key, RegCloseKey);
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys = 0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD cValues; // number of values for key
DWORD cchMaxValue; // longest value name
DWORD cbMaxValueData; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
FILETIME ftLastWriteTime; // last write time
DWORD retCode = RegQueryInfoKey(
key, // key handle
achClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time
for (auto i = 0ul; i < cSubKeys; i++)
{
TCHAR achKey[MAX_KEY_LENGTH];
DWORD cbName = MAX_KEY_LENGTH;
retCode = RegEnumKeyEx(key, i,
achKey,
&cbName,
NULL,
NULL,
NULL,
&ftLastWriteTime);
if (retCode == ERROR_SUCCESS)
{
std::wstring suffix = achKey;
device_id rdid;
std::string a = rsutils::string::windows::win_to_utf( suffix.c_str() );
if (parse_device_id(a, &rdid))
{
for (auto&& did : kvp.second)
{
if (rdid == did)
{
std::wstringstream ss;
ss << prefix << "\\" << suffix << "\\#GLOBAL\\Device Parameters";
auto path = ss.str();
action(rdid, path);
}
}
}
}
}
}
}
}
// Heuristic that determines how many media-pins UVC device is expected to have
static int number_of_mediapins(const std::string& pid, const std::string& mi)
{
if (mi == "00")
{
// D405 has 3 media-pins
if (equal(pid, "0b5b")) return 3;
else return 2; // D400 has two
}
return 1; // RGB has one
}
bool is_running_as_admin()
{
BOOL result = FALSE;
PSID admin_group = NULL;
SID_IDENTIFIER_AUTHORITY ntauthority = SECURITY_NT_AUTHORITY;
if (!AllocateAndInitializeSid(
&ntauthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&admin_group))
{
rs2::log(RS2_LOG_SEVERITY_WARN, "Unable to query permissions - AllocateAndInitializeSid failed");
return false;
}
std::shared_ptr<void> raii(admin_group, FreeSid);
if (!CheckTokenMembership(NULL, admin_group, &result))
{
rs2::log(RS2_LOG_SEVERITY_WARN, "Unable to query permissions - CheckTokenMembership failed");
return false;
}
return result == TRUE;
}
void enable_metadata_with_new_admin_process()
{
wchar_t szPath[MAX_PATH];
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
{
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.lpVerb = L"runas";
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpFile = szPath;
sei.hwnd = NULL;
sei.nShow = SW_NORMAL;
auto cmd_line = get_command_line_param();
std::wstring wcmd(cmd_line.begin(), cmd_line.end());
sei.lpParameters = wcmd.c_str();
if (!ShellExecuteEx(&sei))
{
auto errstr = std::system_category().message(GetLastError());
std::string msg = "Unable to elevate to admin privilege to enable metadata! " + errstr;
rs2::log(RS2_LOG_SEVERITY_WARN, msg.c_str());
}
else
{
WaitForSingleObject(sei.hProcess, INFINITE);
DWORD exitCode = 0;
GetExitCodeProcess(sei.hProcess, &exitCode);
CloseHandle(sei.hProcess);
if (exitCode)
throw std::runtime_error("Failed to set metadata registry keys!");
}
}
else
{
rs2::log(RS2_LOG_SEVERITY_WARN, "Unable to fetch module name!");
}
}
bool is_enabled(std::string id) const override
{
device_id did;
if( ! parse_device_id( id, &did ) )
// If it's not parsed, it's not a valid USB device; return the default
return super::is_enabled( id );
bool res = false;
foreach_device_path({ did }, [&res, did](const device_id&, std::wstring path) {
HKEY key;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &key) == ERROR_SUCCESS)
{
// Don't forget to release in the end:
std::shared_ptr<void> raii(key, RegCloseKey);
bool found = true;
DWORD len = sizeof(DWORD);//size of data
for (int i = 0; i < number_of_mediapins(did.pid, did.mi); i++)
{
std::wstringstream ss; ss << L"MetadataBufferSizeInKB" << i;
std::wstring metadatakey = ss.str();
DWORD MetadataBufferSizeInKB = 0;
if (RegQueryValueEx(
key,
metadatakey.c_str(),
NULL,
NULL,
(LPBYTE)(&MetadataBufferSizeInKB),
&len) != ERROR_SUCCESS)
rs2::log(RS2_LOG_SEVERITY_DEBUG, "Unable to read metadata registry key!");
found = found && MetadataBufferSizeInKB;
}
if (found) res = true;
}
});
return res;
}
void enable_metadata() override
{
if (!is_running_as_admin())
{
enable_metadata_with_new_admin_process();
}
else
{
std::vector<device_id> dids;
rs2::context ctx;
auto list = ctx.query_devices();
for (uint32_t i = 0; i < list.size(); i++)
{
try
{
rs2::device dev = list[i];
if (dev.supports(RS2_CAMERA_INFO_PRODUCT_LINE))
{
std::string product = dev.get_info(RS2_CAMERA_INFO_PRODUCT_LINE);
if (can_support_metadata(product))
{
for (auto sen : dev.query_sensors())
{
if (sen.supports(RS2_CAMERA_INFO_PHYSICAL_PORT))
{
std::string port = sen.get_info(RS2_CAMERA_INFO_PHYSICAL_PORT);
device_id did;
if (parse_device_id(port, &did))
dids.push_back(did);
}
}
}
}
}
catch (...) {}
}
bool failure = false;
foreach_device_path(dids, [&failure](const device_id& did, std::wstring path) {
HKEY key;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &key) == ERROR_SUCCESS)
{
// Don't forget to release in the end:
std::shared_ptr<void> raii(key, RegCloseKey);
bool found = true;
DWORD len = sizeof(DWORD);//size of data
for (int i = 0; i < number_of_mediapins(did.pid, did.mi); i++)
{
std::wstringstream ss; ss << L"MetadataBufferSizeInKB" << i;
std::wstring metadatakey = ss.str();
DWORD MetadataBufferSizeInKB = 5;
if (RegSetValueEx(key, metadatakey.c_str(), 0, REG_DWORD,
(const BYTE*)&MetadataBufferSizeInKB, sizeof(DWORD)) != ERROR_SUCCESS)
{
rs2::log(RS2_LOG_SEVERITY_DEBUG, "Unable to write metadata registry key!");
failure = true;
}
}
}
});
if (failure) throw std::runtime_error("Unable to write to metadata registry key!");
}
}
};
#endif
metadata_helper& metadata_helper::instance()
{
#ifdef WIN32
static windows_metadata_helper instance;
#else
static metadata_helper instance;
#endif
return instance;
}
}