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.

419 lines
9.9KB

  1. #include <system.hpp>
  2. #include <string.hpp>
  3. #include <thread>
  4. #include <regex>
  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 <sched.h>
  11. #include <execinfo.h> // for backtrace and backtrace_symbols
  12. #include <unistd.h> // for execl
  13. #include <sys/utsname.h>
  14. #endif
  15. #if defined ARCH_MAC
  16. #include <mach/mach_init.h>
  17. #include <mach/thread_act.h>
  18. #endif
  19. #if defined ARCH_WIN
  20. #include <windows.h>
  21. #include <shellapi.h>
  22. #include <processthreadsapi.h>
  23. #include <dbghelp.h>
  24. #endif
  25. #define ZIP_STATIC
  26. #include <zip.h>
  27. namespace rack {
  28. namespace system {
  29. std::list<std::string> getEntries(const std::string& path) {
  30. std::list<std::string> filenames;
  31. DIR* dir = opendir(path.c_str());
  32. if (dir) {
  33. struct dirent* d;
  34. while ((d = readdir(dir))) {
  35. std::string filename = d->d_name;
  36. if (filename == "." || filename == "..")
  37. continue;
  38. filenames.push_back(path + "/" + filename);
  39. }
  40. closedir(dir);
  41. }
  42. filenames.sort();
  43. return filenames;
  44. }
  45. std::list<std::string> getEntriesRecursive(const std::string &path, int depth) {
  46. std::list<std::string> entries = getEntries(path);
  47. if (depth > 0) {
  48. // Don't iterate using iterators because the list will be growing.
  49. size_t limit = entries.size();
  50. auto it = entries.begin();
  51. for (size_t i = 0; i < limit; i++) {
  52. const std::string &entry = *it++;
  53. if (isDirectory(entry)) {
  54. std::list<std::string> subEntries = getEntriesRecursive(entry, depth - 1);
  55. // Append subEntries to entries
  56. entries.splice(entries.end(), subEntries);
  57. }
  58. }
  59. }
  60. return entries;
  61. }
  62. bool isFile(const std::string& path) {
  63. struct stat statbuf;
  64. if (stat(path.c_str(), &statbuf))
  65. return false;
  66. return S_ISREG(statbuf.st_mode);
  67. }
  68. bool isDirectory(const std::string& path) {
  69. struct stat statbuf;
  70. if (stat(path.c_str(), &statbuf))
  71. return false;
  72. return S_ISDIR(statbuf.st_mode);
  73. }
  74. void moveFile(const std::string& srcPath, const std::string& destPath) {
  75. std::remove(destPath.c_str());
  76. // Whether this overwrites existing files is implementation-defined.
  77. // i.e. Mingw64 fails to overwrite.
  78. // This is why we remove the file above.
  79. std::rename(srcPath.c_str(), destPath.c_str());
  80. }
  81. void copyFile(const std::string& srcPath, const std::string& destPath) {
  82. // Open source
  83. FILE* source = fopen(srcPath.c_str(), "rb");
  84. if (!source)
  85. return;
  86. DEFER({
  87. fclose(source);
  88. });
  89. // Open destination
  90. FILE* dest = fopen(destPath.c_str(), "wb");
  91. if (!dest)
  92. return;
  93. DEFER({
  94. fclose(dest);
  95. });
  96. // Copy buffer
  97. const int bufferSize = (1 << 15);
  98. char buffer[bufferSize];
  99. while (1) {
  100. size_t size = fread(buffer, 1, bufferSize, source);
  101. if (size == 0)
  102. break;
  103. size = fwrite(buffer, 1, size, dest);
  104. if (size == 0)
  105. break;
  106. }
  107. }
  108. void createDirectory(const std::string& path) {
  109. #if defined ARCH_WIN
  110. std::wstring pathW = string::toWstring(path);
  111. CreateDirectoryW(pathW.c_str(), NULL);
  112. #else
  113. mkdir(path.c_str(), 0755);
  114. #endif
  115. }
  116. int getLogicalCoreCount() {
  117. return std::thread::hardware_concurrency();
  118. }
  119. void setThreadName(const std::string& name) {
  120. #if defined ARCH_LIN
  121. pthread_setname_np(pthread_self(), name.c_str());
  122. #elif defined ARCH_WIN
  123. // Unsupported on Windows
  124. #endif
  125. }
  126. void setThreadRealTime(bool realTime) {
  127. #if defined ARCH_LIN
  128. int err;
  129. int policy;
  130. struct sched_param param;
  131. if (realTime) {
  132. // Round-robin scheduler policy
  133. policy = SCHED_RR;
  134. param.sched_priority = sched_get_priority_max(policy);
  135. }
  136. else {
  137. // Default scheduler policy
  138. policy = 0;
  139. param.sched_priority = 0;
  140. }
  141. err = pthread_setschedparam(pthread_self(), policy, &param);
  142. assert(!err);
  143. // pthread_getschedparam(pthread_self(), &policy, &param);
  144. // DEBUG("policy %d priority %d", policy, param.sched_priority);
  145. #elif defined ARCH_MAC
  146. // Not yet implemented
  147. #elif defined ARCH_WIN
  148. // Set process class first
  149. if (realTime) {
  150. SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  151. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  152. }
  153. else {
  154. SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
  155. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
  156. }
  157. #endif
  158. }
  159. double getThreadTime() {
  160. #if defined ARCH_LIN
  161. struct timespec ts;
  162. clockid_t cid;
  163. pthread_getcpuclockid(pthread_self(), &cid);
  164. clock_gettime(cid, &ts);
  165. return ts.tv_sec + ts.tv_nsec * 1e-9;
  166. #elif defined ARCH_MAC
  167. mach_port_t thread = mach_thread_self();
  168. mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
  169. thread_basic_info_data_t info;
  170. kern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t) &info, &count);
  171. if (kr != KERN_SUCCESS || (info.flags & TH_FLAGS_IDLE) != 0)
  172. return 0.0;
  173. return info.user_time.seconds + info.user_time.microseconds * 1e-6;
  174. #elif defined ARCH_WIN
  175. // FILETIME creationTime;
  176. // FILETIME exitTime;
  177. // FILETIME kernelTime;
  178. // FILETIME userTime;
  179. // GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime);
  180. // return ((uint64_t(userTime.dwHighDateTime) << 32) + userTime.dwLowDateTime) * 1e-7;
  181. uint64_t cycles;
  182. QueryThreadCycleTime(GetCurrentThread(), &cycles);
  183. // HACK Assume that the RDTSC Time-Step Counter instruction is fixed at 2.5GHz. This should only be within a factor of 2 on all PCs.
  184. const double freq = 2.5e9;
  185. return (double) cycles / freq;
  186. #endif
  187. }
  188. std::string getStackTrace() {
  189. int stackLen = 128;
  190. void* stack[stackLen];
  191. std::string s;
  192. #if defined ARCH_LIN || defined ARCH_MAC
  193. stackLen = backtrace(stack, stackLen);
  194. char** strings = backtrace_symbols(stack, stackLen);
  195. // Skip the first line because it's this function.
  196. for (int i = 1; i < stackLen; i++) {
  197. s += string::f("%d: ", stackLen - i - 1);
  198. std::string line = strings[i];
  199. #if 0
  200. // Parse line
  201. std::regex r(R"((.*)\((.*)\+(.*)\) (.*))");
  202. std::smatch match;
  203. if (std::regex_search(line, match, r)) {
  204. s += match[1].str();
  205. s += "(";
  206. std::string symbol = match[2].str();
  207. // Demangle symbol
  208. char* symbolD = __cxxabiv1::__cxa_demangle(symbol.c_str(), NULL, NULL, NULL);
  209. if (symbolD) {
  210. symbol = symbolD;
  211. free(symbolD);
  212. }
  213. s += symbol;
  214. s += "+";
  215. s += match[3].str();
  216. s += ")";
  217. }
  218. #else
  219. s += line;
  220. #endif
  221. s += "\n";
  222. }
  223. free(strings);
  224. #elif defined ARCH_WIN
  225. HANDLE process = GetCurrentProcess();
  226. SymInitialize(process, NULL, true);
  227. stackLen = CaptureStackBackTrace(0, stackLen, stack, NULL);
  228. SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256, 1);
  229. symbol->MaxNameLen = 255;
  230. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  231. for (int i = 1; i < stackLen; i++) {
  232. SymFromAddr(process, (DWORD64) stack[i], 0, symbol);
  233. s += string::f("%d: %s 0x%0x\n", stackLen - i - 1, symbol->Name, symbol->Address);
  234. }
  235. free(symbol);
  236. #endif
  237. return s;
  238. }
  239. void openBrowser(const std::string& url) {
  240. #if defined ARCH_LIN
  241. std::string command = "xdg-open \"" + url + "\"";
  242. (void) std::system(command.c_str());
  243. #endif
  244. #if defined ARCH_MAC
  245. std::string command = "open \"" + url + "\"";
  246. std::system(command.c_str());
  247. #endif
  248. #if defined ARCH_WIN
  249. std::wstring urlW = string::toWstring(url);
  250. ShellExecuteW(NULL, L"open", urlW.c_str(), NULL, NULL, SW_SHOWDEFAULT);
  251. #endif
  252. }
  253. void openFolder(const std::string& path) {
  254. #if defined ARCH_LIN
  255. std::string command = "xdg-open \"" + path + "\"";
  256. (void) std::system(command.c_str());
  257. #endif
  258. #if defined ARCH_MAC
  259. std::string command = "open \"" + path + "\"";
  260. std::system(command.c_str());
  261. #endif
  262. #if defined ARCH_WIN
  263. std::wstring pathW = string::toWstring(path);
  264. ShellExecuteW(NULL, L"explore", pathW.c_str(), NULL, NULL, SW_SHOWDEFAULT);
  265. #endif
  266. }
  267. void runProcessDetached(const std::string& path) {
  268. #if defined ARCH_WIN
  269. SHELLEXECUTEINFOW shExInfo;
  270. ZeroMemory(&shExInfo, sizeof(shExInfo));
  271. shExInfo.cbSize = sizeof(shExInfo);
  272. shExInfo.lpVerb = L"runas";
  273. std::wstring pathW = string::toWstring(path);
  274. shExInfo.lpFile = pathW.c_str();
  275. shExInfo.nShow = SW_SHOW;
  276. if (ShellExecuteExW(&shExInfo)) {
  277. // Do nothing
  278. }
  279. #else
  280. // Not implemented on Linux or Mac
  281. assert(0);
  282. #endif
  283. }
  284. std::string getOperatingSystemInfo() {
  285. #if defined ARCH_LIN || defined ARCH_MAC
  286. struct utsname u;
  287. uname(&u);
  288. return string::f("%s %s %s %s", u.sysname, u.release, u.version, u.machine);
  289. #elif defined ARCH_WIN
  290. OSVERSIONINFOW info;
  291. ZeroMemory(&info, sizeof(info));
  292. info.dwOSVersionInfoSize = sizeof(info);
  293. GetVersionExW(&info);
  294. // See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers.
  295. return string::f("Windows %u.%u", info.dwMajorVersion, info.dwMinorVersion);
  296. #endif
  297. }
  298. int unzipToFolder(const std::string& zipPath, const std::string& dir) {
  299. int err;
  300. // Open ZIP file
  301. zip_t* za = zip_open(zipPath.c_str(), 0, &err);
  302. if (!za) {
  303. WARN("Could not open ZIP file %s: error %d", zipPath.c_str(), err);
  304. return err;
  305. }
  306. DEFER({
  307. zip_close(za);
  308. });
  309. // Iterate ZIP entries
  310. for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
  311. zip_stat_t zs;
  312. err = zip_stat_index(za, i, 0, &zs);
  313. if (err) {
  314. WARN("zip_stat_index() failed: error %d", err);
  315. return err;
  316. }
  317. std::string path = dir + "/" + zs.name;
  318. if (path[path.size() - 1] == '/') {
  319. // Create directory
  320. system::createDirectory(path);
  321. // HACK
  322. // Create and delete file to update the directory's mtime.
  323. std::string tmpPath = path + "/.tmp";
  324. FILE* tmpFile = fopen(tmpPath.c_str(), "w");
  325. fclose(tmpFile);
  326. std::remove(tmpPath.c_str());
  327. }
  328. else {
  329. // Open ZIP entry
  330. zip_file_t* zf = zip_fopen_index(za, i, 0);
  331. if (!zf) {
  332. WARN("zip_fopen_index() failed");
  333. return -1;
  334. }
  335. DEFER({
  336. zip_fclose(zf);
  337. });
  338. // Create file
  339. FILE* outFile = fopen(path.c_str(), "wb");
  340. if (!outFile) {
  341. WARN("Could not create file %s", path.c_str());
  342. return -1;
  343. }
  344. DEFER({
  345. fclose(outFile);
  346. });
  347. // Read buffer and copy to file
  348. while (true) {
  349. char buffer[1 << 15];
  350. int len = zip_fread(zf, buffer, sizeof(buffer));
  351. if (len <= 0)
  352. break;
  353. fwrite(buffer, 1, len, outFile);
  354. }
  355. }
  356. }
  357. return 0;
  358. }
  359. } // namespace system
  360. } // namespace rack