OSUtils.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*
  2. * Copyright (c)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2025-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #include "../core/Constants.hpp"
  14. #include "../core/Utils.hpp"
  15. #include "../core/Containers.hpp"
  16. #include "OSUtils.hpp"
  17. #include <sys/stat.h>
  18. #ifndef __WINDOWS__
  19. #include <dirent.h>
  20. #include <fcntl.h>
  21. #endif
  22. #include <algorithm>
  23. #include <utility>
  24. #ifdef __GCC__
  25. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  26. #endif
  27. namespace ZeroTier {
  28. #ifdef __APPLE__
  29. static clock_serv_t _machGetRealtimeClock() noexcept
  30. {
  31. clock_serv_t c;
  32. host_get_clock_service(mach_host_self(),CALENDAR_CLOCK,&c);
  33. return c;
  34. }
  35. clock_serv_t OSUtils::s_machRealtimeClock = _machGetRealtimeClock();
  36. #endif
  37. unsigned int OSUtils::ztsnprintf(char *buf,unsigned int len,const char *fmt,...)
  38. {
  39. va_list ap;
  40. va_start(ap,fmt);
  41. int n = (int)vsnprintf(buf,len,fmt,ap);
  42. va_end(ap);
  43. if ((n >= (int)len)||(n < 0)) {
  44. if (len)
  45. buf[len - 1] = (char)0;
  46. throw std::length_error("buf[] overflow");
  47. }
  48. return (unsigned int)n;
  49. }
  50. #ifdef __UNIX_LIKE__
  51. bool OSUtils::redirectUnixOutputs(const char *stdoutPath,const char *stderrPath)
  52. {
  53. int fdout = open(stdoutPath,O_WRONLY|O_CREAT,0600);
  54. if (fdout > 0) {
  55. int fderr;
  56. if (stderrPath) {
  57. fderr = open(stderrPath,O_WRONLY|O_CREAT,0600);
  58. if (fderr <= 0) {
  59. ::close(fdout);
  60. return false;
  61. }
  62. } else fderr = fdout;
  63. ::close(STDOUT_FILENO);
  64. ::close(STDERR_FILENO);
  65. ::dup2(fdout,STDOUT_FILENO);
  66. ::dup2(fderr,STDERR_FILENO);
  67. return true;
  68. }
  69. return false;
  70. }
  71. #endif // __UNIX_LIKE__
  72. Vector<String> OSUtils::listDirectory(const char *path,bool includeDirectories)
  73. {
  74. Vector<String> r;
  75. #ifdef __WINDOWS__
  76. HANDLE hFind;
  77. WIN32_FIND_DATAA ffd;
  78. if ((hFind = FindFirstFileA((String(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
  79. do {
  80. if ( (strcmp(ffd.cFileName,".")) && (strcmp(ffd.cFileName,"..")) && (((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)||(((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)&&(includeDirectories))) )
  81. r.push_back(String(ffd.cFileName));
  82. } while (FindNextFileA(hFind,&ffd));
  83. FindClose(hFind);
  84. }
  85. #else
  86. dirent de;
  87. dirent *dptr;
  88. DIR *d = opendir(path);
  89. if (!d)
  90. return r;
  91. dptr = (struct dirent *)0;
  92. for(;;) {
  93. if (readdir_r(d,&de,&dptr))
  94. break;
  95. if (dptr) {
  96. if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&((dptr->d_type != DT_DIR)||(includeDirectories)))
  97. r.push_back(String(dptr->d_name));
  98. } else break;
  99. }
  100. closedir(d);
  101. #endif
  102. return r;
  103. }
  104. bool OSUtils::rmDashRf(const char *path)
  105. {
  106. #ifdef __WINDOWS__
  107. HANDLE hFind;
  108. WIN32_FIND_DATAA ffd;
  109. if ((hFind = FindFirstFileA((String(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
  110. do {
  111. if ((strcmp(ffd.cFileName,".") != 0)&&(strcmp(ffd.cFileName,"..") != 0)) {
  112. if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  113. if (DeleteFileA((String(path) + ZT_PATH_SEPARATOR_S + ffd.cFileName).c_str()) == FALSE)
  114. return false;
  115. } else {
  116. if (!rmDashRf((String(path) + ZT_PATH_SEPARATOR_S + ffd.cFileName).c_str()))
  117. return false;
  118. }
  119. }
  120. } while (FindNextFileA(hFind,&ffd));
  121. FindClose(hFind);
  122. }
  123. return (RemoveDirectoryA(path) != FALSE);
  124. #else
  125. dirent de;
  126. dirent *dptr;
  127. DIR *d = opendir(path);
  128. if (!d)
  129. return true;
  130. dptr = (struct dirent *)0;
  131. for(;;) {
  132. if (readdir_r(d,&de,&dptr) != 0)
  133. break;
  134. if (!dptr)
  135. break;
  136. if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&(strlen(dptr->d_name) > 0)) {
  137. String p(path);
  138. p.push_back(ZT_PATH_SEPARATOR);
  139. p.append(dptr->d_name);
  140. if (unlink(p.c_str()) != 0) { // unlink first will remove symlinks instead of recursing them
  141. if (!rmDashRf(p.c_str()))
  142. return false;
  143. }
  144. }
  145. }
  146. closedir(d);
  147. return (rmdir(path) == 0);
  148. #endif
  149. }
  150. void OSUtils::lockDownFile(const char *path,bool isDir)
  151. {
  152. #ifdef __UNIX_LIKE__
  153. chmod(path,isDir ? 0700 : 0600);
  154. #else
  155. #ifdef __WINDOWS__
  156. {
  157. STARTUPINFOA startupInfo;
  158. PROCESS_INFORMATION processInfo;
  159. startupInfo.cb = sizeof(startupInfo);
  160. memset(&startupInfo,0,sizeof(STARTUPINFOA));
  161. memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
  162. if (CreateProcessA(NULL,(LPSTR)(String("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /inheritance:d /Q").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
  163. WaitForSingleObject(processInfo.hProcess,INFINITE);
  164. CloseHandle(processInfo.hProcess);
  165. CloseHandle(processInfo.hThread);
  166. }
  167. startupInfo.cb = sizeof(startupInfo);
  168. memset(&startupInfo,0,sizeof(STARTUPINFOA));
  169. memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
  170. if (CreateProcessA(NULL,(LPSTR)(String("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /remove *S-1-5-32-545 /Q").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
  171. WaitForSingleObject(processInfo.hProcess,INFINITE);
  172. CloseHandle(processInfo.hProcess);
  173. CloseHandle(processInfo.hThread);
  174. }
  175. }
  176. #endif
  177. #endif
  178. }
  179. bool OSUtils::fileExists(const char *path,bool followLinks)
  180. {
  181. struct stat s;
  182. #ifdef __UNIX_LIKE__
  183. if (!followLinks)
  184. return (lstat(path,&s) == 0);
  185. #endif
  186. return (stat(path,&s) == 0);
  187. }
  188. bool OSUtils::readFile(const char *path,String &buf)
  189. {
  190. char tmp[16384];
  191. FILE *f = fopen(path,"rb");
  192. if (f) {
  193. for(;;) {
  194. long n = (long)fread(tmp,1,sizeof(tmp),f);
  195. if (n > 0)
  196. buf.append(tmp,n);
  197. else break;
  198. }
  199. fclose(f);
  200. return true;
  201. }
  202. return false;
  203. }
  204. bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len)
  205. {
  206. FILE *f = fopen(path,"wb");
  207. if (f) {
  208. if ((long)fwrite(buf,1,len,f) != (long)len) {
  209. fclose(f);
  210. return false;
  211. } else {
  212. fclose(f);
  213. return true;
  214. }
  215. }
  216. return false;
  217. }
  218. Vector<String> OSUtils::split(const char *s,const char *const sep,const char *esc,const char *quot)
  219. {
  220. Vector<String> fields;
  221. String buf;
  222. if (!esc)
  223. esc = "";
  224. if (!quot)
  225. quot = "";
  226. bool escapeState = false;
  227. char quoteState = 0;
  228. while (*s) {
  229. if (escapeState) {
  230. escapeState = false;
  231. buf.push_back(*s);
  232. } else if (quoteState) {
  233. if (*s == quoteState) {
  234. quoteState = 0;
  235. fields.push_back(buf);
  236. buf.clear();
  237. } else buf.push_back(*s);
  238. } else {
  239. const char *quotTmp;
  240. if (strchr(esc,*s))
  241. escapeState = true;
  242. else if ((buf.size() <= 0)&&((quotTmp = strchr(quot,*s))))
  243. quoteState = *quotTmp;
  244. else if (strchr(sep,*s)) {
  245. if (buf.size() > 0) {
  246. fields.push_back(buf);
  247. buf.clear();
  248. } // else skip runs of separators
  249. } else buf.push_back(*s);
  250. }
  251. ++s;
  252. }
  253. if (buf.size())
  254. fields.push_back(buf);
  255. return fields;
  256. }
  257. ZeroTier::String OSUtils::platformDefaultHomePath()
  258. {
  259. #ifdef __QNAP__
  260. char *cmd = "/sbin/getcfg zerotier Install_Path -f /etc/config/qpkg.conf";
  261. char buf[128];
  262. FILE *fp;
  263. if ((fp = popen(cmd, "r")) == NULL) {
  264. printf("Error opening pipe!\n");
  265. return NULL;
  266. }
  267. while (fgets(buf, 128, fp) != NULL) { }
  268. if(pclose(fp)) {
  269. printf("Command not found or exited with error status\n");
  270. return NULL;
  271. }
  272. String homeDir = String(buf);
  273. homeDir.erase(std::remove(homeDir.begin(), homeDir.end(), '\n'), homeDir.end());
  274. return homeDir;
  275. #endif
  276. // Check for user-defined environment variable before using defaults
  277. #ifdef __WINDOWS__
  278. DWORD bufferSize = 65535;
  279. ZeroTier::String userDefinedPath;
  280. bufferSize = GetEnvironmentVariable("ZEROTIER_HOME", &userDefinedPath[0], bufferSize);
  281. if (bufferSize)
  282. return userDefinedPath;
  283. #else
  284. if(const char* userDefinedPath = getenv("ZEROTIER_HOME"))
  285. return String(userDefinedPath);
  286. #endif
  287. // Finally, resort to using default paths if no user-defined path was provided
  288. #ifdef __UNIX_LIKE__
  289. #ifdef __APPLE__
  290. // /Library/... on Apple
  291. return ZeroTier::String("/Library/Application Support/ZeroTier");
  292. #else
  293. #ifdef __BSD__
  294. // BSD likes /var/db instead of /var/lib
  295. return ZeroTier::String("/var/db/zerotier");
  296. #else
  297. // Use /var/lib for Linux and other *nix
  298. return ZeroTier::String("/var/lib/zerotier");
  299. #endif
  300. #endif
  301. #else // not __UNIX_LIKE__
  302. #ifdef __WINDOWS__
  303. // Look up app data folder on Windows, e.g. C:\ProgramData\...
  304. char buf[16384];
  305. if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_COMMON_APPDATA,NULL,0,buf))) {
  306. ZeroTier::String tmp(buf);
  307. tmp.append("\\ZeroTier");
  308. return tmp;
  309. } else {
  310. return ZeroTier::String("C:\\ZeroTier");
  311. }
  312. #else
  313. return (ZeroTier::String(ZT_PATH_SEPARATOR_S) + "ZeroTier"); // UNKNOWN PLATFORM
  314. #endif
  315. #endif // __UNIX_LIKE__ or not...
  316. }
  317. } // namespace ZeroTier