manifest_file.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. // Copyright (c) 2017-2022, The Khronos Group Inc.
  2. // Copyright (c) 2017-2019 Valve Corporation
  3. // Copyright (c) 2017-2019 LunarG, Inc.
  4. //
  5. // SPDX-License-Identifier: Apache-2.0 OR MIT
  6. //
  7. // Initial Authors: Mark Young <[email protected]>, Dave Houlton <[email protected]>
  8. //
  9. #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
  10. #define _CRT_SECURE_NO_WARNINGS
  11. #endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
  12. #include "manifest_file.hpp"
  13. #ifdef OPENXR_HAVE_COMMON_CONFIG
  14. #include "common_config.h"
  15. #endif // OPENXR_HAVE_COMMON_CONFIG
  16. #include "filesystem_utils.hpp"
  17. #include "loader_platform.hpp"
  18. #include "platform_utils.hpp"
  19. #include "loader_logger.hpp"
  20. #include <json/json.h>
  21. #include <openxr/openxr.h>
  22. #include <algorithm>
  23. #include <cstring>
  24. #include <fstream>
  25. #include <memory>
  26. #include <sstream>
  27. #include <stdexcept>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string>
  31. #include <unordered_map>
  32. #include <utility>
  33. #include <vector>
  34. #ifndef FALLBACK_CONFIG_DIRS
  35. #define FALLBACK_CONFIG_DIRS "/etc/xdg"
  36. #endif // !FALLBACK_CONFIG_DIRS
  37. #ifndef FALLBACK_DATA_DIRS
  38. #define FALLBACK_DATA_DIRS "/usr/local/share:/usr/share"
  39. #endif // !FALLBACK_DATA_DIRS
  40. #ifndef SYSCONFDIR
  41. #define SYSCONFDIR "/etc"
  42. #endif // !SYSCONFDIR
  43. #ifdef XRLOADER_DISABLE_EXCEPTION_HANDLING
  44. #if JSON_USE_EXCEPTIONS
  45. #error \
  46. "Loader is configured to not catch exceptions, but jsoncpp was built with exception-throwing enabled, which could violate the C ABI. One of those two things needs to change."
  47. #endif // JSON_USE_EXCEPTIONS
  48. #endif // !XRLOADER_DISABLE_EXCEPTION_HANDLING
  49. #include "runtime_interface.hpp"
  50. // Utility functions for finding files in the appropriate paths
  51. static inline bool StringEndsWith(const std::string &value, const std::string &ending) {
  52. if (ending.size() > value.size()) {
  53. return false;
  54. }
  55. return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
  56. }
  57. // If the file found is a manifest file name, add it to the out_files manifest list.
  58. static void AddIfJson(const std::string &full_file, std::vector<std::string> &manifest_files) {
  59. if (full_file.empty() || !StringEndsWith(full_file, ".json")) {
  60. return;
  61. }
  62. manifest_files.push_back(full_file);
  63. }
  64. // Check the current path for any manifest files. If the provided search_path is a directory, look for
  65. // all included JSON files in that directory. Otherwise, just check the provided search_path which should
  66. // be a single filename.
  67. static void CheckAllFilesInThePath(const std::string &search_path, bool is_directory_list,
  68. std::vector<std::string> &manifest_files) {
  69. if (FileSysUtilsPathExists(search_path)) {
  70. std::string absolute_path;
  71. if (!is_directory_list) {
  72. // If the file exists, try to add it
  73. if (FileSysUtilsIsRegularFile(search_path)) {
  74. FileSysUtilsGetAbsolutePath(search_path, absolute_path);
  75. AddIfJson(absolute_path, manifest_files);
  76. }
  77. } else {
  78. std::vector<std::string> files;
  79. if (FileSysUtilsFindFilesInPath(search_path, files)) {
  80. for (std::string &cur_file : files) {
  81. std::string relative_path;
  82. FileSysUtilsCombinePaths(search_path, cur_file, relative_path);
  83. if (!FileSysUtilsGetAbsolutePath(relative_path, absolute_path)) {
  84. continue;
  85. }
  86. AddIfJson(absolute_path, manifest_files);
  87. }
  88. }
  89. }
  90. }
  91. }
  92. // Add all manifest files in the provided paths to the manifest_files list. If search_path
  93. // is made up of directory listings (versus direct manifest file names) search each path for
  94. // any manifest files.
  95. static void AddFilesInPath(const std::string &search_path, bool is_directory_list, std::vector<std::string> &manifest_files) {
  96. std::size_t last_found = 0;
  97. std::size_t found = search_path.find_first_of(PATH_SEPARATOR);
  98. std::string cur_search;
  99. // Handle any path listings in the string (separated by the appropriate path separator)
  100. while (found != std::string::npos) {
  101. // substr takes a start index and length.
  102. std::size_t length = found - last_found;
  103. cur_search = search_path.substr(last_found, length);
  104. CheckAllFilesInThePath(cur_search, is_directory_list, manifest_files);
  105. // This works around issue if multiple path separator follow each other directly.
  106. last_found = found;
  107. while (found == last_found) {
  108. last_found = found + 1;
  109. found = search_path.find_first_of(PATH_SEPARATOR, last_found);
  110. }
  111. }
  112. // If there's something remaining in the string, copy it over
  113. if (last_found < search_path.size()) {
  114. cur_search = search_path.substr(last_found);
  115. CheckAllFilesInThePath(cur_search, is_directory_list, manifest_files);
  116. }
  117. }
  118. // Copy all paths listed in the cur_path string into output_path and append the appropriate relative_path onto the end of each.
  119. static void CopyIncludedPaths(bool is_directory_list, const std::string &cur_path, const std::string &relative_path,
  120. std::string &output_path) {
  121. if (!cur_path.empty()) {
  122. std::size_t last_found = 0;
  123. std::size_t found = cur_path.find_first_of(PATH_SEPARATOR);
  124. // Handle any path listings in the string (separated by the appropriate path separator)
  125. while (found != std::string::npos) {
  126. std::size_t length = found - last_found;
  127. output_path += cur_path.substr(last_found, length);
  128. if (is_directory_list && (cur_path[found - 1] != '\\' && cur_path[found - 1] != '/')) {
  129. output_path += DIRECTORY_SYMBOL;
  130. }
  131. output_path += relative_path;
  132. output_path += PATH_SEPARATOR;
  133. last_found = found;
  134. found = cur_path.find_first_of(PATH_SEPARATOR, found + 1);
  135. }
  136. // If there's something remaining in the string, copy it over
  137. size_t last_char = cur_path.size() - 1;
  138. if (last_found != last_char) {
  139. output_path += cur_path.substr(last_found);
  140. if (is_directory_list && (cur_path[last_char] != '\\' && cur_path[last_char] != '/')) {
  141. output_path += DIRECTORY_SYMBOL;
  142. }
  143. output_path += relative_path;
  144. output_path += PATH_SEPARATOR;
  145. }
  146. }
  147. }
  148. // Look for data files in the provided paths, but first check the environment override to determine if we should use that instead.
  149. static void ReadDataFilesInSearchPaths(const std::string &override_env_var, const std::string &relative_path, bool &override_active,
  150. std::vector<std::string> &manifest_files) {
  151. std::string override_path;
  152. std::string search_path;
  153. if (!override_env_var.empty()) {
  154. bool permit_override = true;
  155. #ifndef XR_OS_WINDOWS
  156. if (geteuid() != getuid() || getegid() != getgid()) {
  157. // Don't allow setuid apps to use the env var
  158. permit_override = false;
  159. }
  160. #endif
  161. if (permit_override) {
  162. override_path = PlatformUtilsGetSecureEnv(override_env_var.c_str());
  163. }
  164. }
  165. if (!override_path.empty()) {
  166. CopyIncludedPaths(true, override_path, "", search_path);
  167. override_active = true;
  168. } else {
  169. override_active = false;
  170. #if !defined(XR_OS_WINDOWS) && !defined(XR_OS_ANDROID)
  171. const char home_additional[] = ".local/share/";
  172. // Determine how much space is needed to generate the full search path
  173. // for the current manifest files.
  174. std::string xdg_conf_dirs = PlatformUtilsGetSecureEnv("XDG_CONFIG_DIRS");
  175. std::string xdg_data_dirs = PlatformUtilsGetSecureEnv("XDG_DATA_DIRS");
  176. std::string xdg_data_home = PlatformUtilsGetSecureEnv("XDG_DATA_HOME");
  177. std::string home = PlatformUtilsGetSecureEnv("HOME");
  178. if (xdg_conf_dirs.empty()) {
  179. CopyIncludedPaths(true, FALLBACK_CONFIG_DIRS, relative_path, search_path);
  180. } else {
  181. CopyIncludedPaths(true, xdg_conf_dirs, relative_path, search_path);
  182. }
  183. CopyIncludedPaths(true, SYSCONFDIR, relative_path, search_path);
  184. #if defined(EXTRASYSCONFDIR)
  185. CopyIncludedPaths(true, EXTRASYSCONFDIR, relative_path, search_path);
  186. #endif
  187. if (xdg_data_dirs.empty()) {
  188. CopyIncludedPaths(true, FALLBACK_DATA_DIRS, relative_path, search_path);
  189. } else {
  190. CopyIncludedPaths(true, xdg_data_dirs, relative_path, search_path);
  191. }
  192. if (!xdg_data_home.empty()) {
  193. CopyIncludedPaths(true, xdg_data_home, relative_path, search_path);
  194. } else if (!home.empty()) {
  195. std::string relative_home_path = home_additional;
  196. relative_home_path += relative_path;
  197. CopyIncludedPaths(true, home, relative_home_path, search_path);
  198. }
  199. #else
  200. (void)relative_path;
  201. #endif
  202. }
  203. // Now, parse the paths and add any manifest files found in them.
  204. AddFilesInPath(search_path, true, manifest_files);
  205. }
  206. #ifdef XR_OS_LINUX
  207. // Get an XDG environment variable with a $HOME-relative default
  208. static std::string GetXDGEnvHome(const char *name, const char *fallback_path) {
  209. std::string result = PlatformUtilsGetSecureEnv(name);
  210. if (!result.empty()) {
  211. return result;
  212. }
  213. result = PlatformUtilsGetSecureEnv("HOME");
  214. if (result.empty()) {
  215. return result;
  216. }
  217. result += "/";
  218. result += fallback_path;
  219. return result;
  220. }
  221. // Get an XDG environment variable with absolute defaults
  222. static std::string GetXDGEnvAbsolute(const char *name, const char *fallback_paths) {
  223. std::string result = PlatformUtilsGetSecureEnv(name);
  224. if (!result.empty()) {
  225. return result;
  226. }
  227. return fallback_paths;
  228. }
  229. // Return the first instance of relative_path occurring in an XDG config dir according to standard
  230. // precedence order.
  231. static bool FindXDGConfigFile(const std::string &relative_path, std::string &out) {
  232. out = GetXDGEnvHome("XDG_CONFIG_HOME", ".config");
  233. if (!out.empty()) {
  234. out += "/";
  235. out += relative_path;
  236. LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in XDG_CONFIG_HOME: " + out);
  237. if (FileSysUtilsPathExists(out)) {
  238. return true;
  239. }
  240. }
  241. std::istringstream iss(GetXDGEnvAbsolute("XDG_CONFIG_DIRS", FALLBACK_CONFIG_DIRS));
  242. std::string path;
  243. while (std::getline(iss, path, PATH_SEPARATOR)) {
  244. if (path.empty()) {
  245. continue;
  246. }
  247. out = path;
  248. out += "/";
  249. out += relative_path;
  250. LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in an entry of XDG_CONFIG_DIRS: " + out);
  251. if (FileSysUtilsPathExists(out)) {
  252. return true;
  253. }
  254. }
  255. out = SYSCONFDIR;
  256. out += "/";
  257. out += relative_path;
  258. LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in compiled-in SYSCONFDIR: " + out);
  259. if (FileSysUtilsPathExists(out)) {
  260. return true;
  261. }
  262. #if defined(EXTRASYSCONFDIR)
  263. out = EXTRASYSCONFDIR;
  264. out += "/";
  265. out += relative_path;
  266. LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in compiled-in EXTRASYSCONFDIR: " + out);
  267. if (FileSysUtilsPathExists(out)) {
  268. return true;
  269. }
  270. #endif
  271. out.clear();
  272. return false;
  273. }
  274. #endif
  275. #ifdef XR_OS_WINDOWS
  276. // Look for runtime data files in the provided paths, but first check the environment override to determine
  277. // if we should use that instead.
  278. static void ReadRuntimeDataFilesInRegistry(const std::string &runtime_registry_location,
  279. const std::string &default_runtime_value_name,
  280. std::vector<std::string> &manifest_files) {
  281. HKEY hkey;
  282. DWORD access_flags;
  283. wchar_t value_w[1024];
  284. DWORD value_size_w = sizeof(value_w); // byte size of the buffer.
  285. // Generate the full registry location for the registry information
  286. std::string full_registry_location = OPENXR_REGISTRY_LOCATION;
  287. full_registry_location += std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION));
  288. full_registry_location += runtime_registry_location;
  289. const std::wstring full_registry_location_w = utf8_to_wide(full_registry_location);
  290. const std::wstring default_runtime_value_name_w = utf8_to_wide(default_runtime_value_name);
  291. // Use 64 bit regkey for 64bit application, and use 32 bit regkey in WOW for 32bit application.
  292. access_flags = KEY_QUERY_VALUE;
  293. LONG open_value = RegOpenKeyExW(HKEY_LOCAL_MACHINE, full_registry_location_w.c_str(), 0, access_flags, &hkey);
  294. if (ERROR_SUCCESS != open_value) {
  295. LoaderLogger::LogWarningMessage("",
  296. "ReadRuntimeDataFilesInRegistry - failed to open registry key " + full_registry_location);
  297. } else if (ERROR_SUCCESS != RegGetValueW(hkey, nullptr, default_runtime_value_name_w.c_str(),
  298. RRF_RT_REG_SZ | REG_EXPAND_SZ | RRF_ZEROONFAILURE, NULL,
  299. reinterpret_cast<LPBYTE>(&value_w), &value_size_w)) {
  300. LoaderLogger::LogWarningMessage(
  301. "", "ReadRuntimeDataFilesInRegistry - failed to read registry value " + default_runtime_value_name);
  302. } else {
  303. AddFilesInPath(wide_to_utf8(value_w), false, manifest_files);
  304. }
  305. }
  306. // Look for layer data files in the provided paths, but first check the environment override to determine
  307. // if we should use that instead.
  308. static void ReadLayerDataFilesInRegistry(const std::string &registry_location, std::vector<std::string> &manifest_files) {
  309. const std::wstring full_registry_location_w =
  310. utf8_to_wide(OPENXR_REGISTRY_LOCATION + std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) + registry_location);
  311. auto ReadLayerDataFilesInHive = [&](HKEY hive) {
  312. HKEY hkey;
  313. LONG open_value = RegOpenKeyExW(hive, full_registry_location_w.c_str(), 0, KEY_QUERY_VALUE, &hkey);
  314. if (ERROR_SUCCESS != open_value) {
  315. return false;
  316. }
  317. wchar_t name_w[1024]{};
  318. LONG rtn_value;
  319. DWORD name_size = 1023;
  320. DWORD value;
  321. DWORD value_size = sizeof(value);
  322. DWORD key_index = 0;
  323. while (ERROR_SUCCESS ==
  324. (rtn_value = RegEnumValueW(hkey, key_index++, name_w, &name_size, NULL, NULL, (LPBYTE)&value, &value_size))) {
  325. if (value_size == sizeof(value) && value == 0) {
  326. const std::string filename = wide_to_utf8(name_w);
  327. AddFilesInPath(filename, false, manifest_files);
  328. }
  329. // Reset some items for the next loop
  330. name_size = 1023;
  331. }
  332. RegCloseKey(hkey);
  333. return true;
  334. };
  335. // Do not allow high integrity processes to act on data that can be controlled by medium integrity processes.
  336. const bool readFromCurrentUser = !IsHighIntegrityLevel();
  337. bool found = ReadLayerDataFilesInHive(HKEY_LOCAL_MACHINE);
  338. if (readFromCurrentUser) {
  339. found |= ReadLayerDataFilesInHive(HKEY_CURRENT_USER);
  340. }
  341. if (!found) {
  342. std::string warning_message = "ReadLayerDataFilesInRegistry - failed to read registry location ";
  343. warning_message += registry_location;
  344. warning_message += (readFromCurrentUser ? " in either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER" : " in HKEY_LOCAL_MACHINE");
  345. LoaderLogger::LogWarningMessage("", warning_message);
  346. }
  347. }
  348. #endif // XR_OS_WINDOWS
  349. ManifestFile::ManifestFile(ManifestFileType type, const std::string &filename, const std::string &library_path)
  350. : _filename(filename), _type(type), _library_path(library_path) {}
  351. bool ManifestFile::IsValidJson(const Json::Value &root_node, JsonVersion &version) {
  352. if (root_node["file_format_version"].isNull() || !root_node["file_format_version"].isString()) {
  353. LoaderLogger::LogErrorMessage("", "ManifestFile::IsValidJson - JSON file missing \"file_format_version\"");
  354. return false;
  355. }
  356. std::string file_format = root_node["file_format_version"].asString();
  357. const int num_fields = sscanf(file_format.c_str(), "%u.%u.%u", &version.major, &version.minor, &version.patch);
  358. // Only version 1.0.0 is defined currently. Eventually we may have more version, but
  359. // some of the versions may only be valid for layers or runtimes specifically.
  360. if (num_fields != 3 || version.major != 1 || version.minor != 0 || version.patch != 0) {
  361. std::ostringstream error_ss;
  362. error_ss << "ManifestFile::IsValidJson - JSON \"file_format_version\" " << version.major << "." << version.minor << "."
  363. << version.patch << " is not supported";
  364. LoaderLogger::LogErrorMessage("", error_ss.str());
  365. return false;
  366. }
  367. return true;
  368. }
  369. static void GetExtensionProperties(const std::vector<ExtensionListing> &extensions, std::vector<XrExtensionProperties> &props) {
  370. for (const auto &ext : extensions) {
  371. auto it =
  372. std::find_if(props.begin(), props.end(), [&](XrExtensionProperties &prop) { return prop.extensionName == ext.name; });
  373. if (it != props.end()) {
  374. it->extensionVersion = std::max(it->extensionVersion, ext.extension_version);
  375. } else {
  376. XrExtensionProperties prop = {};
  377. prop.type = XR_TYPE_EXTENSION_PROPERTIES;
  378. prop.next = nullptr;
  379. strncpy(prop.extensionName, ext.name.c_str(), XR_MAX_EXTENSION_NAME_SIZE - 1);
  380. prop.extensionName[XR_MAX_EXTENSION_NAME_SIZE - 1] = '\0';
  381. prop.extensionVersion = ext.extension_version;
  382. props.push_back(prop);
  383. }
  384. }
  385. }
  386. // Return any instance extensions found in the manifest files in the proper form for
  387. // OpenXR (XrExtensionProperties).
  388. void ManifestFile::GetInstanceExtensionProperties(std::vector<XrExtensionProperties> &props) {
  389. GetExtensionProperties(_instance_extensions, props);
  390. }
  391. const std::string &ManifestFile::GetFunctionName(const std::string &func_name) const {
  392. if (!_functions_renamed.empty()) {
  393. auto found = _functions_renamed.find(func_name);
  394. if (found != _functions_renamed.end()) {
  395. return found->second;
  396. }
  397. }
  398. return func_name;
  399. }
  400. RuntimeManifestFile::RuntimeManifestFile(const std::string &filename, const std::string &library_path)
  401. : ManifestFile(MANIFEST_TYPE_RUNTIME, filename, library_path) {}
  402. static void ParseExtension(Json::Value const &ext, std::vector<ExtensionListing> &extensions) {
  403. Json::Value ext_name = ext["name"];
  404. Json::Value ext_version = ext["extension_version"];
  405. // Allow "extension_version" as a String or a UInt to maintain backwards compatibility, even though it should be a String.
  406. // Internal Issue 1411: https://gitlab.khronos.org/openxr/openxr/-/issues/1411
  407. // Internal MR !1867: https://gitlab.khronos.org/openxr/openxr/-/merge_requests/1867
  408. if (ext_name.isString() && (ext_version.isString() || ext_version.isUInt())) {
  409. ExtensionListing ext_listing = {};
  410. ext_listing.name = ext_name.asString();
  411. if (ext_version.isUInt()) {
  412. ext_listing.extension_version = ext_version.asUInt();
  413. } else {
  414. ext_listing.extension_version = atoi(ext_version.asString().c_str());
  415. }
  416. extensions.push_back(ext_listing);
  417. }
  418. }
  419. void ManifestFile::ParseCommon(Json::Value const &root_node) {
  420. const Json::Value &inst_exts = root_node["instance_extensions"];
  421. if (!inst_exts.isNull() && inst_exts.isArray()) {
  422. for (const auto &ext : inst_exts) {
  423. ParseExtension(ext, _instance_extensions);
  424. }
  425. }
  426. const Json::Value &funcs_renamed = root_node["functions"];
  427. if (!funcs_renamed.isNull() && !funcs_renamed.empty()) {
  428. for (Json::ValueConstIterator func_it = funcs_renamed.begin(); func_it != funcs_renamed.end(); ++func_it) {
  429. if (!(*func_it).isString()) {
  430. LoaderLogger::LogWarningMessage(
  431. "", "ManifestFile::ParseCommon " + _filename + " \"functions\" section contains non-string values.");
  432. continue;
  433. }
  434. std::string original_name = func_it.key().asString();
  435. std::string new_name = (*func_it).asString();
  436. _functions_renamed.emplace(original_name, new_name);
  437. }
  438. }
  439. }
  440. void RuntimeManifestFile::CreateIfValid(std::string const &filename,
  441. std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
  442. std::ifstream json_stream(filename, std::ifstream::in);
  443. LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::CreateIfValid - attempting to load " + filename);
  444. std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
  445. if (!json_stream.is_open()) {
  446. error_ss << "failed to open " << filename << ". Does it exist?";
  447. LoaderLogger::LogErrorMessage("", error_ss.str());
  448. return;
  449. }
  450. Json::CharReaderBuilder builder;
  451. std::string errors;
  452. Json::Value root_node = Json::nullValue;
  453. if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) {
  454. error_ss << "failed to parse " << filename << ".";
  455. if (!errors.empty()) {
  456. error_ss << " (Error message: " << errors << ")";
  457. }
  458. error_ss << " Is it a valid runtime manifest file?";
  459. LoaderLogger::LogErrorMessage("", error_ss.str());
  460. return;
  461. }
  462. CreateIfValid(root_node, filename, manifest_files);
  463. }
  464. void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std::string &filename,
  465. std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
  466. std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
  467. JsonVersion file_version = {};
  468. if (!ManifestFile::IsValidJson(root_node, file_version)) {
  469. error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
  470. LoaderLogger::LogErrorMessage("", error_ss.str());
  471. return;
  472. }
  473. const Json::Value &runtime_root_node = root_node["runtime"];
  474. // The Runtime manifest file needs the "runtime" root as well as a sub-node for "library_path". If any of those aren't there,
  475. // fail.
  476. if (runtime_root_node.isNull() || runtime_root_node["library_path"].isNull() || !runtime_root_node["library_path"].isString()) {
  477. error_ss << filename << " is missing required fields. Verify all proper fields exist.";
  478. LoaderLogger::LogErrorMessage("", error_ss.str());
  479. return;
  480. }
  481. std::string lib_path = runtime_root_node["library_path"].asString();
  482. // If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
  483. // global library path.
  484. if (lib_path.find('\\') != std::string::npos || lib_path.find('/') != std::string::npos) {
  485. // If the library_path is an absolute path, just use that if it exists
  486. if (FileSysUtilsIsAbsolutePath(lib_path)) {
  487. if (!FileSysUtilsPathExists(lib_path)) {
  488. error_ss << filename << " library " << lib_path << " does not appear to exist";
  489. LoaderLogger::LogErrorMessage("", error_ss.str());
  490. return;
  491. }
  492. } else {
  493. // Otherwise, treat the library path as a relative path based on the JSON file.
  494. std::string canonical_path;
  495. std::string combined_path;
  496. std::string file_parent;
  497. // Search relative to the real manifest file, not relative to the symlink
  498. if (!FileSysUtilsGetCanonicalPath(filename, canonical_path)) {
  499. // Give relative to the non-canonical path a chance
  500. canonical_path = filename;
  501. }
  502. if (!FileSysUtilsGetParentPath(canonical_path, file_parent) ||
  503. !FileSysUtilsCombinePaths(file_parent, lib_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
  504. error_ss << filename << " library " << combined_path << " does not appear to exist";
  505. LoaderLogger::LogErrorMessage("", error_ss.str());
  506. return;
  507. }
  508. lib_path = combined_path;
  509. }
  510. }
  511. // Add this runtime manifest file
  512. manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path));
  513. // Add any extensions to it after the fact.
  514. // Handle any renamed functions
  515. manifest_files.back()->ParseCommon(runtime_root_node);
  516. }
  517. // Find all manifest files in the appropriate search paths/registries for the given type.
  518. XrResult RuntimeManifestFile::FindManifestFiles(std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
  519. XrResult result = XR_SUCCESS;
  520. std::string filename = PlatformUtilsGetSecureEnv(OPENXR_RUNTIME_JSON_ENV_VAR);
  521. if (!filename.empty()) {
  522. LoaderLogger::LogInfoMessage(
  523. "", "RuntimeManifestFile::FindManifestFiles - using environment variable override runtime file " + filename);
  524. } else {
  525. #ifdef XR_OS_WINDOWS
  526. std::vector<std::string> filenames;
  527. ReadRuntimeDataFilesInRegistry("", "ActiveRuntime", filenames);
  528. if (filenames.size() == 0) {
  529. LoaderLogger::LogErrorMessage(
  530. "", "RuntimeManifestFile::FindManifestFiles - failed to find active runtime file in registry");
  531. return XR_ERROR_RUNTIME_UNAVAILABLE;
  532. }
  533. if (filenames.size() > 1) {
  534. LoaderLogger::LogWarningMessage(
  535. "", "RuntimeManifestFile::FindManifestFiles - found too many default runtime files in registry");
  536. }
  537. filename = filenames[0];
  538. LoaderLogger::LogInfoMessage("",
  539. "RuntimeManifestFile::FindManifestFiles - using registry-specified runtime file " + filename);
  540. #elif defined(XR_OS_LINUX)
  541. const std::string relative_path =
  542. "openxr/" + std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) + "/active_runtime.json";
  543. if (!FindXDGConfigFile(relative_path, filename)) {
  544. LoaderLogger::LogErrorMessage(
  545. "", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
  546. return XR_ERROR_RUNTIME_UNAVAILABLE;
  547. }
  548. #else
  549. #if defined(XR_KHR_LOADER_INIT_SUPPORT)
  550. Json::Value virtualManifest;
  551. result = GetPlatformRuntimeVirtualManifest(virtualManifest);
  552. if (XR_SUCCESS == result) {
  553. RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files);
  554. return result;
  555. }
  556. #endif // defined(XR_KHR_LOADER_INIT_SUPPORT)
  557. if (!PlatformGetGlobalRuntimeFileName(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) {
  558. LoaderLogger::LogErrorMessage(
  559. "", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
  560. return XR_ERROR_RUNTIME_UNAVAILABLE;
  561. }
  562. result = XR_SUCCESS;
  563. LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::FindManifestFiles - using global runtime file " + filename);
  564. #endif
  565. }
  566. RuntimeManifestFile::CreateIfValid(filename, manifest_files);
  567. return result;
  568. }
  569. ApiLayerManifestFile::ApiLayerManifestFile(ManifestFileType type, const std::string &filename, const std::string &layer_name,
  570. const std::string &description, const JsonVersion &api_version,
  571. const uint32_t &implementation_version, const std::string &library_path)
  572. : ManifestFile(type, filename, library_path),
  573. _api_version(api_version),
  574. _layer_name(layer_name),
  575. _description(description),
  576. _implementation_version(implementation_version) {}
  577. void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename,
  578. std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
  579. std::ifstream json_stream(filename, std::ifstream::in);
  580. std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
  581. if (!json_stream.is_open()) {
  582. error_ss << "failed to open " << filename << ". Does it exist?";
  583. LoaderLogger::LogErrorMessage("", error_ss.str());
  584. return;
  585. }
  586. Json::CharReaderBuilder builder;
  587. std::string errors;
  588. Json::Value root_node = Json::nullValue;
  589. if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) {
  590. error_ss << "failed to parse " << filename << ".";
  591. if (!errors.empty()) {
  592. error_ss << " (Error message: " << errors << ")";
  593. }
  594. error_ss << " Is it a valid layer manifest file?";
  595. LoaderLogger::LogErrorMessage("", error_ss.str());
  596. return;
  597. }
  598. JsonVersion file_version = {};
  599. if (!ManifestFile::IsValidJson(root_node, file_version)) {
  600. error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
  601. LoaderLogger::LogErrorMessage("", error_ss.str());
  602. return;
  603. }
  604. Json::Value layer_root_node = root_node["api_layer"];
  605. // The API Layer manifest file needs the "api_layer" root as well as other sub-nodes.
  606. // If any of those aren't there, fail.
  607. if (layer_root_node.isNull() || layer_root_node["name"].isNull() || !layer_root_node["name"].isString() ||
  608. layer_root_node["api_version"].isNull() || !layer_root_node["api_version"].isString() ||
  609. layer_root_node["library_path"].isNull() || !layer_root_node["library_path"].isString() ||
  610. layer_root_node["implementation_version"].isNull() || !layer_root_node["implementation_version"].isString()) {
  611. error_ss << filename << " is missing required fields. Verify all proper fields exist.";
  612. LoaderLogger::LogErrorMessage("", error_ss.str());
  613. return;
  614. }
  615. if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) {
  616. bool enabled = true;
  617. // Implicit layers require the disable environment variable.
  618. if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].isString()) {
  619. error_ss << "Implicit layer " << filename << " is missing \"disable_environment\"";
  620. LoaderLogger::LogErrorMessage("", error_ss.str());
  621. return;
  622. }
  623. // Check if there's an enable environment variable provided
  624. if (!layer_root_node["enable_environment"].isNull() && layer_root_node["enable_environment"].isString()) {
  625. std::string env_var = layer_root_node["enable_environment"].asString();
  626. // If it's not set in the environment, disable the layer
  627. if (!PlatformUtilsGetEnvSet(env_var.c_str())) {
  628. enabled = false;
  629. }
  630. }
  631. // Check for the disable environment variable, which must be provided in the JSON
  632. std::string env_var = layer_root_node["disable_environment"].asString();
  633. // If the env var is set, disable the layer. Disable env var overrides enable above
  634. if (PlatformUtilsGetEnvSet(env_var.c_str())) {
  635. enabled = false;
  636. }
  637. // Not enabled, so pretend like it isn't even there.
  638. if (!enabled) {
  639. error_ss << "Implicit layer " << filename << " is disabled";
  640. LoaderLogger::LogInfoMessage("", error_ss.str());
  641. return;
  642. }
  643. }
  644. std::string layer_name = layer_root_node["name"].asString();
  645. std::string api_version_string = layer_root_node["api_version"].asString();
  646. JsonVersion api_version = {};
  647. const int num_fields = sscanf(api_version_string.c_str(), "%u.%u", &api_version.major, &api_version.minor);
  648. api_version.patch = 0;
  649. if ((num_fields != 2) || (api_version.major == 0 && api_version.minor == 0) ||
  650. api_version.major > XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) {
  651. error_ss << "layer " << filename << " has invalid API Version. Skipping layer.";
  652. LoaderLogger::LogWarningMessage("", error_ss.str());
  653. return;
  654. }
  655. uint32_t implementation_version = atoi(layer_root_node["implementation_version"].asString().c_str());
  656. std::string library_path = layer_root_node["library_path"].asString();
  657. // If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
  658. // global library path.
  659. if (library_path.find('\\') != std::string::npos || library_path.find('/') != std::string::npos) {
  660. // If the library_path is an absolute path, just use that if it exists
  661. if (FileSysUtilsIsAbsolutePath(library_path)) {
  662. if (!FileSysUtilsPathExists(library_path)) {
  663. error_ss << filename << " library " << library_path << " does not appear to exist";
  664. LoaderLogger::LogErrorMessage("", error_ss.str());
  665. return;
  666. }
  667. } else {
  668. // Otherwise, treat the library path as a relative path based on the JSON file.
  669. std::string combined_path;
  670. std::string file_parent;
  671. if (!FileSysUtilsGetParentPath(filename, file_parent) ||
  672. !FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
  673. error_ss << filename << " library " << combined_path << " does not appear to exist";
  674. LoaderLogger::LogErrorMessage("", error_ss.str());
  675. return;
  676. }
  677. library_path = combined_path;
  678. }
  679. }
  680. std::string description;
  681. if (!layer_root_node["description"].isNull() && layer_root_node["description"].isString()) {
  682. description = layer_root_node["description"].asString();
  683. }
  684. // Add this layer manifest file
  685. manifest_files.emplace_back(
  686. new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
  687. // Add any extensions to it after the fact.
  688. manifest_files.back()->ParseCommon(layer_root_node);
  689. }
  690. void ApiLayerManifestFile::PopulateApiLayerProperties(XrApiLayerProperties &props) const {
  691. props.layerVersion = _implementation_version;
  692. props.specVersion = XR_MAKE_VERSION(_api_version.major, _api_version.minor, _api_version.patch);
  693. strncpy(props.layerName, _layer_name.c_str(), XR_MAX_API_LAYER_NAME_SIZE - 1);
  694. if (_layer_name.size() >= XR_MAX_API_LAYER_NAME_SIZE - 1) {
  695. props.layerName[XR_MAX_API_LAYER_NAME_SIZE - 1] = '\0';
  696. }
  697. strncpy(props.description, _description.c_str(), XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1);
  698. if (_description.size() >= XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1) {
  699. props.description[XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1] = '\0';
  700. }
  701. }
  702. // Find all layer manifest files in the appropriate search paths/registries for the given type.
  703. XrResult ApiLayerManifestFile::FindManifestFiles(ManifestFileType type,
  704. std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
  705. std::string relative_path;
  706. std::string override_env_var;
  707. std::string registry_location;
  708. // Add the appropriate top-level folders for the relative path. These should be
  709. // the string "openxr/" followed by the API major version as a string.
  710. relative_path = OPENXR_RELATIVE_PATH;
  711. relative_path += std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION));
  712. switch (type) {
  713. case MANIFEST_TYPE_IMPLICIT_API_LAYER:
  714. relative_path += OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH;
  715. override_env_var = "";
  716. #ifdef XR_OS_WINDOWS
  717. registry_location = OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION;
  718. #endif
  719. break;
  720. case MANIFEST_TYPE_EXPLICIT_API_LAYER:
  721. relative_path += OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH;
  722. override_env_var = OPENXR_API_LAYER_PATH_ENV_VAR;
  723. #ifdef XR_OS_WINDOWS
  724. registry_location = OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION;
  725. #endif
  726. break;
  727. default:
  728. LoaderLogger::LogErrorMessage("", "ApiLayerManifestFile::FindManifestFiles - unknown manifest file requested");
  729. return XR_ERROR_FILE_ACCESS_ERROR;
  730. }
  731. bool override_active = false;
  732. std::vector<std::string> filenames;
  733. ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames);
  734. #ifdef XR_OS_WINDOWS
  735. // Read the registry if the override wasn't active.
  736. if (!override_active) {
  737. ReadLayerDataFilesInRegistry(registry_location, filenames);
  738. }
  739. #endif
  740. for (std::string &cur_file : filenames) {
  741. ApiLayerManifestFile::CreateIfValid(type, cur_file, manifest_files);
  742. }
  743. return XR_SUCCESS;
  744. }