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.

859 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. // Don't restore timestamps
  429. int flags = 0;
  430. archive_write_disk_set_options(disk, flags);
  431. DEFER({archive_write_close(disk);});
  432. // Iterate archive
  433. for (;;) {
  434. // Get next entry
  435. struct archive_entry* entry;
  436. r = archive_read_next_header(a, &entry);
  437. if (r == ARCHIVE_EOF)
  438. break;
  439. if (r < ARCHIVE_OK)
  440. throw Exception("Unarchiver could not read entry from archive: %s", archive_error_string(a));
  441. // Convert relative pathname to absolute based on dirPath
  442. std::string entryPath = archive_entry_pathname(entry);
  443. // DEBUG("entryPath: %s", entryPath.c_str());
  444. if (!fs::u8path(entryPath).is_relative())
  445. throw Exception("Unarchiver does not support absolute tar paths: %s", entryPath.c_str());
  446. entryPath = (fs::u8path(dirPath) / fs::u8path(entryPath)).generic_u8string();
  447. #if defined ARCH_WIN
  448. archive_entry_copy_pathname_w(entry, string::UTF8toUTF16(entryPath).c_str());
  449. #else
  450. archive_entry_set_pathname(entry, entryPath.c_str());
  451. #endif
  452. // Write entry to disk
  453. r = archive_write_header(disk, entry);
  454. if (r < ARCHIVE_OK)
  455. throw Exception("Unarchiver could not write file to dir: %s", archive_error_string(disk));
  456. // Copy data to file
  457. for (;;) {
  458. const void* buf;
  459. size_t size;
  460. int64_t offset;
  461. // Read data from archive
  462. r = archive_read_data_block(a, &buf, &size, &offset);
  463. if (r == ARCHIVE_EOF)
  464. break;
  465. if (r < ARCHIVE_OK)
  466. throw Exception("Unarchiver could not read data from archive: %s", archive_error_string(a));
  467. // Write data to file
  468. r = archive_write_data_block(disk, buf, size, offset);
  469. if (r < ARCHIVE_OK)
  470. throw Exception("Unarchiver could not write data to file: %s", archive_error_string(disk));
  471. }
  472. // Close file
  473. r = archive_write_finish_entry(disk);
  474. if (r < ARCHIVE_OK)
  475. throw Exception("Unarchiver could not close file: %s", archive_error_string(disk));
  476. }
  477. }
  478. void unarchiveToDirectory(const std::string& archivePath, const std::string& dirPath) {
  479. unarchiveToDirectory(archivePath, NULL, dirPath);
  480. }
  481. void unarchiveToDirectory(const std::vector<uint8_t>& archiveData, const std::string& dirPath) {
  482. unarchiveToDirectory("", &archiveData, dirPath);
  483. }
  484. int getLogicalCoreCount() {
  485. return std::thread::hardware_concurrency();
  486. }
  487. void setThreadName(const std::string& name) {
  488. #if defined ARCH_LIN
  489. pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
  490. #elif defined ARCH_MAC
  491. // Not supported (yet) on Mac
  492. #elif defined ARCH_WIN
  493. // Not supported on Windows
  494. #endif
  495. }
  496. std::string getStackTrace() {
  497. int stackLen = 128;
  498. void* stack[stackLen];
  499. std::string s;
  500. #if defined ARCH_LIN || defined ARCH_MAC
  501. stackLen = backtrace(stack, stackLen);
  502. char** strings = backtrace_symbols(stack, stackLen);
  503. // Skip the first line because it's this function.
  504. for (int i = 1; i < stackLen; i++) {
  505. s += string::f("%d: ", stackLen - i - 1);
  506. std::string line = strings[i];
  507. #if 0
  508. // Parse line
  509. std::regex r(R"((.*)\((.*)\+(.*)\) (.*))");
  510. std::smatch match;
  511. if (std::regex_search(line, match, r)) {
  512. s += match[1].str();
  513. s += "(";
  514. std::string symbol = match[2].str();
  515. // Demangle symbol
  516. char* symbolD = __cxxabiv1::__cxa_demangle(symbol.c_str(), NULL, NULL, NULL);
  517. if (symbolD) {
  518. symbol = symbolD;
  519. free(symbolD);
  520. }
  521. s += symbol;
  522. s += "+";
  523. s += match[3].str();
  524. s += ")";
  525. }
  526. #else
  527. s += line;
  528. #endif
  529. s += "\n";
  530. }
  531. free(strings);
  532. #elif defined ARCH_WIN
  533. HANDLE process = GetCurrentProcess();
  534. SymInitialize(process, NULL, true);
  535. stackLen = CaptureStackBackTrace(0, stackLen, stack, NULL);
  536. SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256, 1);
  537. symbol->MaxNameLen = 255;
  538. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  539. for (int i = 1; i < stackLen; i++) {
  540. SymFromAddr(process, (DWORD64) stack[i], 0, symbol);
  541. s += string::f("%d: %s 0x%" PRIx64 "\n", stackLen - i - 1, symbol->Name, symbol->Address);
  542. }
  543. free(symbol);
  544. #endif
  545. return s;
  546. }
  547. #if defined ARCH_WIN
  548. static int64_t startCounter = 0;
  549. static double counterTime = 0.0;
  550. #endif
  551. #if defined ARCH_LIN
  552. static int64_t startTime = 0;
  553. #endif
  554. #if defined ARCH_MAC
  555. static int64_t startCounter = 0;
  556. static double counterTime = 0.0;
  557. #endif
  558. static void initTime() {
  559. #if defined ARCH_WIN
  560. LARGE_INTEGER counter;
  561. QueryPerformanceCounter(&counter);
  562. startCounter = counter.QuadPart;
  563. LARGE_INTEGER frequency;
  564. QueryPerformanceFrequency(&frequency);
  565. counterTime = 1.0 / frequency.QuadPart;
  566. #endif
  567. #if defined ARCH_LIN
  568. struct timespec ts;
  569. clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  570. startTime = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
  571. #endif
  572. #if defined ARCH_MAC
  573. startCounter = mach_absolute_time();
  574. mach_timebase_info_data_t tb;
  575. mach_timebase_info(&tb);
  576. counterTime = 1e-9 * (double) tb.numer / tb.denom;
  577. #endif
  578. }
  579. double getTime() {
  580. #if defined ARCH_WIN
  581. LARGE_INTEGER counter;
  582. QueryPerformanceCounter(&counter);
  583. return (counter.QuadPart - startCounter) * counterTime;
  584. #endif
  585. #if defined ARCH_LIN
  586. struct timespec ts;
  587. clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  588. int64_t time = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
  589. return (time - startTime) / 1e9;
  590. #endif
  591. #if defined ARCH_MAC
  592. int64_t counter = mach_absolute_time();
  593. return (counter - startCounter) * counterTime;
  594. #endif
  595. }
  596. double getUnixTime() {
  597. // This is not guaranteed to return the time since 1970 in C++11. (It only does in C++20).
  598. // However, it does on all platforms I care about.
  599. auto duration = std::chrono::system_clock::now().time_since_epoch();
  600. return std::chrono::duration<double>(duration).count();
  601. }
  602. double getThreadTime() {
  603. #if defined ARCH_LIN
  604. struct timespec ts;
  605. clockid_t cid;
  606. pthread_getcpuclockid(pthread_self(), &cid);
  607. clock_gettime(cid, &ts);
  608. return ts.tv_sec + ts.tv_nsec * 1e-9;
  609. #elif defined ARCH_MAC
  610. mach_port_t thread = mach_thread_self();
  611. mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
  612. thread_basic_info_data_t info;
  613. kern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t) &info, &count);
  614. if (kr != KERN_SUCCESS || (info.flags & TH_FLAGS_IDLE) != 0)
  615. return 0.0;
  616. return info.user_time.seconds + info.user_time.microseconds * 1e-6;
  617. #elif defined ARCH_WIN
  618. FILETIME creationTime;
  619. FILETIME exitTime;
  620. FILETIME kernelTime;
  621. FILETIME userTime;
  622. if (GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime) == 0)
  623. return 0.0;
  624. return ((uint64_t(userTime.dwHighDateTime) << 32) + userTime.dwLowDateTime) * 1e-7;
  625. #endif
  626. }
  627. std::string getOperatingSystemInfo() {
  628. #if defined ARCH_LIN
  629. struct utsname u;
  630. uname(&u);
  631. return string::f("%s %s %s %s", u.sysname, u.release, u.version, u.machine);
  632. #elif defined ARCH_MAC
  633. // From https://opensource.apple.com/source/cctools/cctools-973.0.1/libstuff/macosx_deployment_target.c.auto.html
  634. char osversion[32];
  635. int osversion_name[2] = {CTL_KERN, KERN_OSRELEASE};
  636. size_t osversion_len = sizeof(osversion) - 1;
  637. if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) != 0) return "Mac";
  638. int major = 0;
  639. int minor = 0;
  640. if (sscanf(osversion, "%d.%d", &major, &minor) != 2)
  641. return "Mac";
  642. // Try to match version numbers to retail versions
  643. if (major >= 20) {
  644. major -= 9;
  645. return string::f("Mac %d.%d", major, minor);
  646. }
  647. else {
  648. major -= 4;
  649. return string::f("Mac 10.%d.%d", major, minor);
  650. }
  651. #elif defined ARCH_WIN
  652. OSVERSIONINFOW info;
  653. ZeroMemory(&info, sizeof(info));
  654. info.dwOSVersionInfoSize = sizeof(info);
  655. GetVersionExW(&info);
  656. // See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers.
  657. return string::f("Windows %lu.%lu", info.dwMajorVersion, info.dwMinorVersion);
  658. #endif
  659. }
  660. void openBrowser(const std::string& url) {
  661. if (url.empty())
  662. return;
  663. std::string urlL = url;
  664. std::thread t([=] {
  665. #if defined ARCH_LIN
  666. std::string command = "xdg-open \"" + urlL + "\"";
  667. (void) std::system(command.c_str());
  668. #endif
  669. #if defined ARCH_MAC
  670. std::string command = "open \"" + urlL + "\"";
  671. std::system(command.c_str());
  672. #endif
  673. #if defined ARCH_WIN
  674. ShellExecuteW(NULL, L"open", string::UTF8toUTF16(urlL).c_str(), NULL, NULL, SW_SHOWDEFAULT);
  675. #endif
  676. });
  677. t.detach();
  678. }
  679. void openDirectory(const std::string& path) {
  680. if (path.empty())
  681. return;
  682. std::string pathL = path;
  683. std::thread t([=] {
  684. #if defined ARCH_LIN
  685. std::string command = "xdg-open \"" + pathL + "\"";
  686. (void) std::system(command.c_str());
  687. #endif
  688. #if defined ARCH_MAC
  689. std::string command = "open \"" + pathL + "\"";
  690. std::system(command.c_str());
  691. #endif
  692. #if defined ARCH_WIN
  693. ShellExecuteW(NULL, L"explore", string::UTF8toUTF16(pathL).c_str(), NULL, NULL, SW_SHOWDEFAULT);
  694. #endif
  695. });
  696. t.detach();
  697. }
  698. void runProcessDetached(const std::string& path) {
  699. #if defined ARCH_WIN
  700. SHELLEXECUTEINFOW shExInfo;
  701. ZeroMemory(&shExInfo, sizeof(shExInfo));
  702. shExInfo.cbSize = sizeof(shExInfo);
  703. shExInfo.lpVerb = L"runas";
  704. std::wstring pathW = string::UTF8toUTF16(path);
  705. shExInfo.lpFile = pathW.c_str();
  706. shExInfo.nShow = SW_SHOW;
  707. if (ShellExecuteExW(&shExInfo)) {
  708. // Do nothing
  709. }
  710. #else
  711. // Not implemented on Linux or Mac
  712. assert(0);
  713. #endif
  714. }
  715. void init() {
  716. initTime();
  717. }
  718. } // namespace system
  719. } // namespace rack