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.

858 lines
21KB

  1. #include <thread>
  2. #include <regex>
  3. #include <chrono>
  4. #include <ghc/filesystem.hpp>
  5. #include <dirent.h>
  6. #include <sys/stat.h>
  7. #include <cxxabi.h> // for __cxxabiv1::__cxa_demangle
  8. #if defined ARCH_LIN || defined ARCH_MAC
  9. #include <pthread.h>
  10. #include <time.h> // for clock_gettime
  11. #include <sched.h>
  12. #include <execinfo.h> // for backtrace and backtrace_symbols
  13. #include <unistd.h> // for execl
  14. #include <sys/utsname.h>
  15. #endif
  16. #if defined ARCH_MAC
  17. #include <mach/mach_init.h>
  18. #include <mach/thread_act.h>
  19. #include <mach/mach_time.h>
  20. #include <sys/sysctl.h>
  21. #endif
  22. #if defined ARCH_WIN
  23. #include <windows.h>
  24. #include <shellapi.h>
  25. #include <processthreadsapi.h>
  26. #include <dbghelp.h>
  27. #endif
  28. #include <archive.h>
  29. #include <archive_entry.h>
  30. #if defined ARCH_MAC
  31. #include <locale.h>
  32. #endif
  33. #include <system.hpp>
  34. #include <string.hpp>
  35. /*
  36. In C++17, this will be `std::filesystem`
  37. Important: When using `fs::path`, always convert strings to/from UTF-8 using
  38. fs::path p = fs::u8path(s);
  39. std::string s = p.generic_u8string();
  40. */
  41. namespace fs = ghc::filesystem;
  42. namespace rack {
  43. namespace system {
  44. std::string join(const std::string& path1, const std::string& path2) {
  45. return (fs::u8path(path1) / fs::u8path(path2)).generic_u8string();
  46. }
  47. static void appendEntries(std::vector<std::string>& entries, const fs::path& dir, int depth) {
  48. try {
  49. for (const auto& entry : fs::directory_iterator(dir)) {
  50. entries.push_back(entry.path().generic_u8string());
  51. // Recurse if depth > 0 (limited recursion) or depth < 0 (infinite recursion).
  52. if (depth != 0) {
  53. if (entry.is_directory()) {
  54. appendEntries(entries, entry.path(), depth - 1);
  55. }
  56. }
  57. }
  58. }
  59. catch (fs::filesystem_error& e) {}
  60. }
  61. std::vector<std::string> getEntries(const std::string& dirPath, int depth) {
  62. std::vector<std::string> entries;
  63. appendEntries(entries, fs::u8path(dirPath), depth);
  64. return entries;
  65. }
  66. bool exists(const std::string& path) {
  67. try {
  68. return fs::exists(fs::u8path(path));
  69. }
  70. catch (fs::filesystem_error& e) {
  71. return false;
  72. }
  73. }
  74. bool isFile(const std::string& path) {
  75. try {
  76. return fs::is_regular_file(fs::u8path(path));
  77. }
  78. catch (fs::filesystem_error& e) {
  79. return false;
  80. }
  81. }
  82. bool isDirectory(const std::string& path) {
  83. try {
  84. return fs::is_directory(fs::u8path(path));
  85. }
  86. catch (fs::filesystem_error& e) {
  87. return false;
  88. }
  89. }
  90. uint64_t getFileSize(const std::string& path) {
  91. try {
  92. return fs::file_size(fs::u8path(path));
  93. }
  94. catch (fs::filesystem_error& e) {
  95. return 0;
  96. }
  97. }
  98. bool rename(const std::string& srcPath, const std::string& destPath) {
  99. try {
  100. fs::rename(fs::u8path(srcPath), fs::u8path(destPath));
  101. return true;
  102. }
  103. catch (fs::filesystem_error& e) {
  104. return false;
  105. }
  106. }
  107. bool copy(const std::string& srcPath, const std::string& destPath) {
  108. try {
  109. fs::copy(fs::u8path(srcPath), fs::u8path(destPath), fs::copy_options::recursive | fs::copy_options::overwrite_existing);
  110. return true;
  111. }
  112. catch (fs::filesystem_error& e) {
  113. return false;
  114. }
  115. }
  116. bool createDirectory(const std::string& path) {
  117. try {
  118. return fs::create_directory(fs::u8path(path));
  119. }
  120. catch (fs::filesystem_error& e) {
  121. return false;
  122. }
  123. }
  124. bool createDirectories(const std::string& path) {
  125. try {
  126. return fs::create_directories(fs::u8path(path));
  127. }
  128. catch (fs::filesystem_error& e) {
  129. return false;
  130. }
  131. }
  132. bool createSymbolicLink(const std::string& target, const std::string& link) {
  133. try {
  134. fs::create_symlink(fs::u8path(target), fs::u8path(link));
  135. return true;
  136. }
  137. catch (fs::filesystem_error& e) {
  138. return false;
  139. }
  140. }
  141. bool remove(const std::string& path) {
  142. try {
  143. return fs::remove(fs::u8path(path));
  144. }
  145. catch (fs::filesystem_error& e) {
  146. return false;
  147. }
  148. }
  149. int removeRecursively(const std::string& path) {
  150. try {
  151. return fs::remove_all(fs::u8path(path));
  152. }
  153. catch (fs::filesystem_error& e) {
  154. return 0;
  155. }
  156. }
  157. std::string getWorkingDirectory() {
  158. try {
  159. return fs::current_path().generic_u8string();
  160. }
  161. catch (fs::filesystem_error& e) {
  162. return "";
  163. }
  164. }
  165. void setWorkingDirectory(const std::string& path) {
  166. try {
  167. fs::current_path(fs::u8path(path));
  168. }
  169. catch (fs::filesystem_error& e) {
  170. // Do nothing
  171. }
  172. }
  173. std::string getTempDirectory() {
  174. try {
  175. return fs::temp_directory_path().generic_u8string();
  176. }
  177. catch (fs::filesystem_error& e) {
  178. return "";
  179. }
  180. }
  181. std::string getAbsolute(const std::string& path) {
  182. try {
  183. return fs::absolute(fs::u8path(path)).generic_u8string();
  184. }
  185. catch (fs::filesystem_error& e) {
  186. return "";
  187. }
  188. }
  189. std::string getCanonical(const std::string& path) {
  190. try {
  191. return fs::canonical(fs::u8path(path)).generic_u8string();
  192. }
  193. catch (fs::filesystem_error& e) {
  194. return "";
  195. }
  196. }
  197. std::string getDirectory(const std::string& path) {
  198. try {
  199. return fs::u8path(path).parent_path().generic_u8string();
  200. }
  201. catch (fs::filesystem_error& e) {
  202. return "";
  203. }
  204. }
  205. std::string getFilename(const std::string& path) {
  206. try {
  207. return fs::u8path(path).filename().generic_u8string();
  208. }
  209. catch (fs::filesystem_error& e) {
  210. return "";
  211. }
  212. }
  213. std::string getStem(const std::string& path) {
  214. try {
  215. return fs::u8path(path).stem().generic_u8string();
  216. }
  217. catch (fs::filesystem_error& e) {
  218. return "";
  219. }
  220. }
  221. std::string getExtension(const std::string& path) {
  222. try {
  223. return fs::u8path(path).extension().generic_u8string();
  224. }
  225. catch (fs::filesystem_error& e) {
  226. return "";
  227. }
  228. }
  229. std::vector<uint8_t> readFile(const std::string& path) {
  230. std::vector<uint8_t> data;
  231. FILE* f = std::fopen(path.c_str(), "rb");
  232. if (!f)
  233. throw Exception("Cannot read file %s", path.c_str());
  234. DEFER({
  235. std::fclose(f);
  236. });
  237. // Get file size so we can make a single allocation
  238. std::fseek(f, 0, SEEK_END);
  239. size_t len = std::ftell(f);
  240. std::fseek(f, 0, SEEK_SET);
  241. data.resize(len);
  242. std::fread(data.data(), 1, len, f);
  243. return data;
  244. }
  245. void writeFile(const std::string& path, const std::vector<uint8_t>& data) {
  246. FILE* f = std::fopen(path.c_str(), "wb");
  247. if (!f)
  248. throw Exception("Cannot create file %s", path.c_str());
  249. DEFER({
  250. std::fclose(f);
  251. });
  252. std::fwrite(data.data(), 1, data.size(), f);
  253. }
  254. /** Returns `p` in relative path form, relative to `base`
  255. Limitation: `p` must be a descendant of `base`. Doesn't support adding `../` to the return path.
  256. */
  257. static std::string getRelativePath(std::string path, std::string base) {
  258. try {
  259. path = fs::absolute(fs::u8path(path)).generic_u8string();
  260. base = fs::absolute(fs::u8path(base)).generic_u8string();
  261. }
  262. catch (fs::filesystem_error& e) {
  263. throw Exception("%s", e.what());
  264. }
  265. if (path.size() < base.size())
  266. throw Exception("getRelativePath() error: path is shorter than base");
  267. if (!std::equal(base.begin(), base.end(), path.begin()))
  268. throw Exception("getRelativePath() error: path does not begin with base");
  269. // If path == base, this correctly returns "."
  270. return "." + std::string(path.begin() + base.size(), path.end());
  271. }
  272. static la_ssize_t archiveWriteVectorCallback(struct archive* a, void* client_data, const void* buffer, size_t length) {
  273. assert(client_data);
  274. std::vector<uint8_t>& data = *((std::vector<uint8_t>*) client_data);
  275. uint8_t* buf = (uint8_t*) buffer;
  276. data.insert(data.end(), buf, buf + length);
  277. return length;
  278. }
  279. static void archiveDirectory(const std::string& archivePath, std::vector<uint8_t>* archiveData, const std::string& dirPath, int compressionLevel) {
  280. // Based on minitar.c create() in libarchive examples
  281. int r;
  282. // Open archive for writing
  283. struct archive* a = archive_write_new();
  284. DEFER({archive_write_free(a);});
  285. // For some reason libarchive adds 10k of padding to archive_write_open() (but not archive_write_open_filename()) unless this is set to 0.
  286. archive_write_set_bytes_per_block(a, 0);
  287. archive_write_set_format_ustar(a);
  288. archive_write_add_filter_zstd(a);
  289. assert(0 <= compressionLevel && compressionLevel <= 19);
  290. r = archive_write_set_filter_option(a, NULL, "compression-level", std::to_string(compressionLevel).c_str());
  291. if (r < ARCHIVE_OK)
  292. throw Exception("Archiver could not set filter option: %s", archive_error_string(a));
  293. if (archiveData) {
  294. // Open vector
  295. archive_write_open(a, (void*) archiveData, NULL, archiveWriteVectorCallback, NULL);
  296. }
  297. else {
  298. // Open file
  299. #if defined ARCH_WIN
  300. r = archive_write_open_filename_w(a, string::UTF8toUTF16(archivePath).c_str());
  301. #else
  302. r = archive_write_open_filename(a, archivePath.c_str());
  303. #endif
  304. if (r < ARCHIVE_OK)
  305. throw Exception("Archiver could not open archive %s for writing: %s", archivePath.c_str(), archive_error_string(a));
  306. }
  307. DEFER({archive_write_close(a);});
  308. // Open dir for reading
  309. struct archive* disk = archive_read_disk_new();
  310. DEFER({archive_read_free(disk);});
  311. #if defined ARCH_WIN
  312. r = archive_read_disk_open_w(disk, string::UTF8toUTF16(dirPath).c_str());
  313. #else
  314. r = archive_read_disk_open(disk, dirPath.c_str());
  315. #endif
  316. if (r < ARCHIVE_OK)
  317. throw Exception("Archiver could not open dir %s for reading: %s", dirPath.c_str(), archive_error_string(a));
  318. DEFER({archive_read_close(a);});
  319. // Iterate dir
  320. for (;;) {
  321. struct archive_entry* entry = archive_entry_new();
  322. DEFER({archive_entry_free(entry);});
  323. r = archive_read_next_header2(disk, entry);
  324. if (r == ARCHIVE_EOF)
  325. break;
  326. if (r < ARCHIVE_OK)
  327. throw Exception("Archiver could not get next entry from archive: %s", archive_error_string(disk));
  328. // Recurse dirs
  329. archive_read_disk_descend(disk);
  330. // Convert absolute path to relative path
  331. std::string entryPath;
  332. #if defined ARCH_WIN
  333. entryPath = string::UTF16toUTF8(archive_entry_pathname_w(entry));
  334. #else
  335. entryPath = archive_entry_pathname(entry);
  336. #endif
  337. entryPath = getRelativePath(entryPath, dirPath);
  338. #if defined ARCH_WIN
  339. // FIXME This doesn't seem to set UTF-8 paths on Windows.
  340. archive_entry_copy_pathname_w(entry, string::UTF8toUTF16(entryPath).c_str());
  341. #else
  342. archive_entry_set_pathname(entry, entryPath.c_str());
  343. #endif
  344. // Write file to archive
  345. r = archive_write_header(a, entry);
  346. if (r < ARCHIVE_OK)
  347. throw Exception("Archiver could not write entry to archive: %s", archive_error_string(a));
  348. // Manually copy data
  349. #if defined ARCH_WIN
  350. std::string entrySourcePath = string::UTF16toUTF8(archive_entry_sourcepath_w(entry));
  351. #else
  352. std::string entrySourcePath = archive_entry_sourcepath(entry);
  353. #endif
  354. FILE* f = std::fopen(entrySourcePath.c_str(), "rb");
  355. DEFER({std::fclose(f);});
  356. char buf[1 << 16];
  357. ssize_t len;
  358. while ((len = std::fread(buf, 1, sizeof(buf), f)) > 0) {
  359. archive_write_data(a, buf, len);
  360. }
  361. }
  362. }
  363. void archiveDirectory(const std::string& archivePath, const std::string& dirPath, int compressionLevel) {
  364. archiveDirectory(archivePath, NULL, dirPath, compressionLevel);
  365. }
  366. std::vector<uint8_t> archiveDirectory(const std::string& dirPath, int compressionLevel) {
  367. std::vector<uint8_t> archiveData;
  368. archiveDirectory("", &archiveData, dirPath, compressionLevel);
  369. return archiveData;
  370. }
  371. struct ArchiveReadVectorData {
  372. const std::vector<uint8_t>* data = NULL;
  373. size_t pos = 0;
  374. };
  375. static la_ssize_t archiveReadVectorCallback(struct archive *a, void* client_data, const void** buffer) {
  376. assert(client_data);
  377. ArchiveReadVectorData* arvd = (ArchiveReadVectorData*) client_data;
  378. assert(arvd->data);
  379. const std::vector<uint8_t>& data = *arvd->data;
  380. *buffer = &data[arvd->pos];
  381. // Read up to some block size of bytes
  382. size_t len = std::min(data.size() - arvd->pos, size_t(1 << 16));
  383. arvd->pos += len;
  384. return len;
  385. }
  386. static void unarchiveToDirectory(const std::string& archivePath, const std::vector<uint8_t>* archiveData, const std::string& dirPath) {
  387. #if defined ARCH_MAC
  388. // libarchive depends on locale so set thread
  389. // If locale is not found, returns NULL which resets thread to global locale
  390. locale_t loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", NULL);
  391. locale_t oldLoc = uselocale(loc);
  392. freelocale(loc);
  393. DEFER({
  394. uselocale(oldLoc);
  395. });
  396. #endif
  397. // Based on minitar.c extract() in libarchive examples
  398. int r;
  399. // Open archive for reading
  400. struct archive* a = archive_read_new();
  401. if (!a)
  402. throw Exception("Unarchiver could not be created");
  403. DEFER({archive_read_free(a);});
  404. archive_read_support_filter_zstd(a);
  405. // archive_read_support_filter_all(a);
  406. archive_read_support_format_tar(a);
  407. // archive_read_support_format_all(a);
  408. ArchiveReadVectorData arvd;
  409. if (archiveData) {
  410. // Open vector
  411. arvd.data = archiveData;
  412. archive_read_open(a, &arvd, NULL, archiveReadVectorCallback, NULL);
  413. }
  414. else {
  415. // Open file
  416. #if defined ARCH_WIN
  417. r = archive_read_open_filename_w(a, string::UTF8toUTF16(archivePath).c_str(), 1 << 16);
  418. #else
  419. r = archive_read_open_filename(a, archivePath.c_str(), 1 << 16);
  420. #endif
  421. if (r < ARCHIVE_OK)
  422. throw Exception("Unarchiver could not open archive %s: %s", archivePath.c_str(), archive_error_string(a));
  423. }
  424. DEFER({archive_read_close(a);});
  425. // Open dir for writing
  426. struct archive* disk = archive_write_disk_new();
  427. DEFER({archive_write_free(disk);});
  428. int flags = ARCHIVE_EXTRACT_TIME;
  429. archive_write_disk_set_options(disk, flags);
  430. DEFER({archive_write_close(disk);});
  431. // Iterate archive
  432. for (;;) {
  433. // Get next entry
  434. struct archive_entry* entry;
  435. r = archive_read_next_header(a, &entry);
  436. if (r == ARCHIVE_EOF)
  437. break;
  438. if (r < ARCHIVE_OK)
  439. throw Exception("Unarchiver could not read entry from archive: %s", archive_error_string(a));
  440. // Convert relative pathname to absolute based on dirPath
  441. std::string entryPath = archive_entry_pathname(entry);
  442. // DEBUG("entryPath: %s", entryPath.c_str());
  443. if (!fs::u8path(entryPath).is_relative())
  444. throw Exception("Unarchiver does not support absolute tar paths: %s", entryPath.c_str());
  445. entryPath = (fs::u8path(dirPath) / fs::u8path(entryPath)).generic_u8string();
  446. #if defined ARCH_WIN
  447. archive_entry_copy_pathname_w(entry, string::UTF8toUTF16(entryPath).c_str());
  448. #else
  449. archive_entry_set_pathname(entry, entryPath.c_str());
  450. #endif
  451. // Write entry to disk
  452. r = archive_write_header(disk, entry);
  453. if (r < ARCHIVE_OK)
  454. throw Exception("Unarchiver could not write file to dir: %s", archive_error_string(disk));
  455. // Copy data to file
  456. for (;;) {
  457. const void* buf;
  458. size_t size;
  459. int64_t offset;
  460. // Read data from archive
  461. r = archive_read_data_block(a, &buf, &size, &offset);
  462. if (r == ARCHIVE_EOF)
  463. break;
  464. if (r < ARCHIVE_OK)
  465. throw Exception("Unarchiver could not read data from archive: %s", archive_error_string(a));
  466. // Write data to file
  467. r = archive_write_data_block(disk, buf, size, offset);
  468. if (r < ARCHIVE_OK)
  469. throw Exception("Unarchiver could not write data to file: %s", archive_error_string(disk));
  470. }
  471. // Close file
  472. r = archive_write_finish_entry(disk);
  473. if (r < ARCHIVE_OK)
  474. throw Exception("Unarchiver could not close file: %s", archive_error_string(disk));
  475. }
  476. }
  477. void unarchiveToDirectory(const std::string& archivePath, const std::string& dirPath) {
  478. unarchiveToDirectory(archivePath, NULL, dirPath);
  479. }
  480. void unarchiveToDirectory(const std::vector<uint8_t>& archiveData, const std::string& dirPath) {
  481. unarchiveToDirectory("", &archiveData, dirPath);
  482. }
  483. int getLogicalCoreCount() {
  484. return std::thread::hardware_concurrency();
  485. }
  486. void setThreadName(const std::string& name) {
  487. #if defined ARCH_LIN
  488. pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
  489. #elif defined ARCH_MAC
  490. // Not supported (yet) on Mac
  491. #elif defined ARCH_WIN
  492. // Not supported on Windows
  493. #endif
  494. }
  495. std::string getStackTrace() {
  496. int stackLen = 128;
  497. void* stack[stackLen];
  498. std::string s;
  499. #if defined ARCH_LIN || defined ARCH_MAC
  500. stackLen = backtrace(stack, stackLen);
  501. char** strings = backtrace_symbols(stack, stackLen);
  502. // Skip the first line because it's this function.
  503. for (int i = 1; i < stackLen; i++) {
  504. s += string::f("%d: ", stackLen - i - 1);
  505. std::string line = strings[i];
  506. #if 0
  507. // Parse line
  508. std::regex r(R"((.*)\((.*)\+(.*)\) (.*))");
  509. std::smatch match;
  510. if (std::regex_search(line, match, r)) {
  511. s += match[1].str();
  512. s += "(";
  513. std::string symbol = match[2].str();
  514. // Demangle symbol
  515. char* symbolD = __cxxabiv1::__cxa_demangle(symbol.c_str(), NULL, NULL, NULL);
  516. if (symbolD) {
  517. symbol = symbolD;
  518. free(symbolD);
  519. }
  520. s += symbol;
  521. s += "+";
  522. s += match[3].str();
  523. s += ")";
  524. }
  525. #else
  526. s += line;
  527. #endif
  528. s += "\n";
  529. }
  530. free(strings);
  531. #elif defined ARCH_WIN
  532. HANDLE process = GetCurrentProcess();
  533. SymInitialize(process, NULL, true);
  534. stackLen = CaptureStackBackTrace(0, stackLen, stack, NULL);
  535. SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256, 1);
  536. symbol->MaxNameLen = 255;
  537. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  538. for (int i = 1; i < stackLen; i++) {
  539. SymFromAddr(process, (DWORD64) stack[i], 0, symbol);
  540. s += string::f("%d: %s 0x%" PRIx64 "\n", stackLen - i - 1, symbol->Name, symbol->Address);
  541. }
  542. free(symbol);
  543. #endif
  544. return s;
  545. }
  546. #if defined ARCH_WIN
  547. static int64_t startCounter = 0;
  548. static double counterTime = 0.0;
  549. #endif
  550. #if defined ARCH_LIN
  551. static int64_t startTime = 0;
  552. #endif
  553. #if defined ARCH_MAC
  554. static int64_t startCounter = 0;
  555. static double counterTime = 0.0;
  556. #endif
  557. static void initTime() {
  558. #if defined ARCH_WIN
  559. LARGE_INTEGER counter;
  560. QueryPerformanceCounter(&counter);
  561. startCounter = counter.QuadPart;
  562. LARGE_INTEGER frequency;
  563. QueryPerformanceFrequency(&frequency);
  564. counterTime = 1.0 / frequency.QuadPart;
  565. #endif
  566. #if defined ARCH_LIN
  567. struct timespec ts;
  568. clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  569. startTime = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
  570. #endif
  571. #if defined ARCH_MAC
  572. startCounter = mach_absolute_time();
  573. mach_timebase_info_data_t tb;
  574. mach_timebase_info(&tb);
  575. counterTime = 1e-9 * (double) tb.numer / tb.denom;
  576. #endif
  577. }
  578. double getTime() {
  579. #if defined ARCH_WIN
  580. LARGE_INTEGER counter;
  581. QueryPerformanceCounter(&counter);
  582. return (counter.QuadPart - startCounter) * counterTime;
  583. #endif
  584. #if defined ARCH_LIN
  585. struct timespec ts;
  586. clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  587. int64_t time = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
  588. return (time - startTime) / 1e9;
  589. #endif
  590. #if defined ARCH_MAC
  591. int64_t counter = mach_absolute_time();
  592. return (counter - startCounter) * counterTime;
  593. #endif
  594. }
  595. double getUnixTime() {
  596. // This is not guaranteed to return the time since 1970 in C++11. (It only does in C++20).
  597. // However, it does on all platforms I care about.
  598. auto duration = std::chrono::system_clock::now().time_since_epoch();
  599. return std::chrono::duration<double>(duration).count();
  600. }
  601. double getThreadTime() {
  602. #if defined ARCH_LIN
  603. struct timespec ts;
  604. clockid_t cid;
  605. pthread_getcpuclockid(pthread_self(), &cid);
  606. clock_gettime(cid, &ts);
  607. return ts.tv_sec + ts.tv_nsec * 1e-9;
  608. #elif defined ARCH_MAC
  609. mach_port_t thread = mach_thread_self();
  610. mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
  611. thread_basic_info_data_t info;
  612. kern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t) &info, &count);
  613. if (kr != KERN_SUCCESS || (info.flags & TH_FLAGS_IDLE) != 0)
  614. return 0.0;
  615. return info.user_time.seconds + info.user_time.microseconds * 1e-6;
  616. #elif defined ARCH_WIN
  617. FILETIME creationTime;
  618. FILETIME exitTime;
  619. FILETIME kernelTime;
  620. FILETIME userTime;
  621. if (GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime) == 0)
  622. return 0.0;
  623. return ((uint64_t(userTime.dwHighDateTime) << 32) + userTime.dwLowDateTime) * 1e-7;
  624. #endif
  625. }
  626. std::string getOperatingSystemInfo() {
  627. #if defined ARCH_LIN
  628. struct utsname u;
  629. uname(&u);
  630. return string::f("%s %s %s %s", u.sysname, u.release, u.version, u.machine);
  631. #elif defined ARCH_MAC
  632. // From https://opensource.apple.com/source/cctools/cctools-973.0.1/libstuff/macosx_deployment_target.c.auto.html
  633. char osversion[32];
  634. int osversion_name[2] = {CTL_KERN, KERN_OSRELEASE};
  635. size_t osversion_len = sizeof(osversion) - 1;
  636. if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) != 0) return "Mac";
  637. int major = 0;
  638. int minor = 0;
  639. if (sscanf(osversion, "%d.%d", &major, &minor) != 2)
  640. return "Mac";
  641. // Try to match version numbers to retail versions
  642. if (major >= 20) {
  643. major -= 9;
  644. return string::f("Mac %d.%d", major, minor);
  645. }
  646. else {
  647. major -= 4;
  648. return string::f("Mac 10.%d.%d", major, minor);
  649. }
  650. #elif defined ARCH_WIN
  651. OSVERSIONINFOW info;
  652. ZeroMemory(&info, sizeof(info));
  653. info.dwOSVersionInfoSize = sizeof(info);
  654. GetVersionExW(&info);
  655. // See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers.
  656. return string::f("Windows %lu.%lu", info.dwMajorVersion, info.dwMinorVersion);
  657. #endif
  658. }
  659. void openBrowser(const std::string& url) {
  660. if (url.empty())
  661. return;
  662. std::string urlL = url;
  663. std::thread t([=] {
  664. #if defined ARCH_LIN
  665. std::string command = "xdg-open \"" + urlL + "\"";
  666. (void) std::system(command.c_str());
  667. #endif
  668. #if defined ARCH_MAC
  669. std::string command = "open \"" + urlL + "\"";
  670. std::system(command.c_str());
  671. #endif
  672. #if defined ARCH_WIN
  673. ShellExecuteW(NULL, L"open", string::UTF8toUTF16(urlL).c_str(), NULL, NULL, SW_SHOWDEFAULT);
  674. #endif
  675. });
  676. t.detach();
  677. }
  678. void openDirectory(const std::string& path) {
  679. if (path.empty())
  680. return;
  681. std::string pathL = path;
  682. std::thread t([=] {
  683. #if defined ARCH_LIN
  684. std::string command = "xdg-open \"" + pathL + "\"";
  685. (void) std::system(command.c_str());
  686. #endif
  687. #if defined ARCH_MAC
  688. std::string command = "open \"" + pathL + "\"";
  689. std::system(command.c_str());
  690. #endif
  691. #if defined ARCH_WIN
  692. ShellExecuteW(NULL, L"explore", string::UTF8toUTF16(pathL).c_str(), NULL, NULL, SW_SHOWDEFAULT);
  693. #endif
  694. });
  695. t.detach();
  696. }
  697. void runProcessDetached(const std::string& path) {
  698. #if defined ARCH_WIN
  699. SHELLEXECUTEINFOW shExInfo;
  700. ZeroMemory(&shExInfo, sizeof(shExInfo));
  701. shExInfo.cbSize = sizeof(shExInfo);
  702. shExInfo.lpVerb = L"runas";
  703. std::wstring pathW = string::UTF8toUTF16(path);
  704. shExInfo.lpFile = pathW.c_str();
  705. shExInfo.nShow = SW_SHOW;
  706. if (ShellExecuteExW(&shExInfo)) {
  707. // Do nothing
  708. }
  709. #else
  710. // Not implemented on Linux or Mac
  711. assert(0);
  712. #endif
  713. }
  714. void init() {
  715. initTime();
  716. }
  717. } // namespace system
  718. } // namespace rack