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.

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