Electroneum
util.cpp
Go to the documentation of this file.
1 // Copyrights(c) 2017-2021, The Electroneum Project
2 // Copyrights(c) 2014-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
31 
32 #include <unistd.h>
33 #include <cstdio>
34 
35 #ifdef __GLIBC__
36 #include <gnu/libc-version.h>
37 #endif
38 
39 #ifdef __GLIBC__
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/resource.h>
43 #include <unistd.h>
44 #include <dirent.h>
45 #include <string.h>
46 #include <ctype.h>
47 #include <string>
48 #endif
49 
50 //tools::is_hdd
51 #ifdef __GLIBC__
52  #include <sstream>
53  #include <sys/sysmacros.h>
54  #include <fstream>
55 #endif
56 
57 #include "unbound.h"
58 
59 #include "include_base_utils.h"
60 #include "file_io_utils.h"
61 #include "wipeable_string.h"
62 #include "misc_os_dependent.h"
63 using namespace epee;
64 
65 #include "crypto/crypto.h"
66 #include "util.h"
67 #include "stack_trace.h"
68 #include "memwipe.h"
69 #include "cryptonote_config.h"
70 #include "net/http_client.h" // epee::net_utils::...
71 
72 #ifdef WIN32
73 #ifndef STRSAFE_NO_DEPRECATE
74 #define STRSAFE_NO_DEPRECATE
75 #endif
76  #include <windows.h>
77  #include <shlobj.h>
78  #include <strsafe.h>
79 #else
80  #include <sys/file.h>
81  #include <sys/utsname.h>
82  #include <sys/stat.h>
83 #endif
84 #include <boost/filesystem.hpp>
85 #include <boost/algorithm/string.hpp>
86 #include <boost/asio.hpp>
87 #include <boost/format.hpp>
88 #include <openssl/sha.h>
89 
90 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
91 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "util"
92 
93 namespace
94 {
95 
96 #ifndef _WIN32
97 static int flock_exnb(int fd)
98 {
99  struct flock fl;
100  int ret;
101 
102  memset(&fl, 0, sizeof(fl));
103  fl.l_type = F_WRLCK;
104  fl.l_whence = SEEK_SET;
105  fl.l_start = 0;
106  fl.l_len = 0;
107  ret = fcntl(fd, F_SETLK, &fl);
108  if (ret < 0)
109  MERROR("Error locking fd " << fd << ": " << errno << " (" << strerror(errno) << ")");
110  return ret;
111 }
112 #endif
113 
114 }
115 
116 namespace tools
117 {
118  std::function<void(int)> signal_handler::m_handler;
119 
120  private_file::private_file() noexcept : m_handle(), m_filename() {}
121 
122  private_file::private_file(std::FILE* handle, std::string&& filename) noexcept
123  : m_handle(handle), m_filename(std::move(filename)) {}
124 
125  private_file private_file::create(std::string name)
126  {
127 #ifdef WIN32
128  struct close_handle
129  {
130  void operator()(HANDLE handle) const noexcept
131  {
132  CloseHandle(handle);
133  }
134  };
135 
136  std::unique_ptr<void, close_handle> process = nullptr;
137  {
138  HANDLE temp{};
139  const bool fail = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0;
140  process.reset(temp);
141  if (fail)
142  return {};
143  }
144 
145  DWORD sid_size = 0;
146  GetTokenInformation(process.get(), TokenOwner, nullptr, 0, std::addressof(sid_size));
147  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
148  return {};
149 
150  std::unique_ptr<char[]> sid{new char[sid_size]};
151  if (!GetTokenInformation(process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size)))
152  return {};
153 
154  const PSID psid = reinterpret_cast<const PTOKEN_OWNER>(sid.get())->Owner;
155  const DWORD daclSize =
156  sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD);
157 
158  const std::unique_ptr<char[]> dacl{new char[daclSize]};
159  if (!InitializeAcl(reinterpret_cast<PACL>(dacl.get()), daclSize, ACL_REVISION))
160  return {};
161 
162  if (!AddAccessAllowedAce(reinterpret_cast<PACL>(dacl.get()), ACL_REVISION, (READ_CONTROL | FILE_GENERIC_READ | DELETE), psid))
163  return {};
164 
165  SECURITY_DESCRIPTOR descriptor{};
166  if (!InitializeSecurityDescriptor(std::addressof(descriptor), SECURITY_DESCRIPTOR_REVISION))
167  return {};
168 
169  if (!SetSecurityDescriptorDacl(std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false))
170  return {};
171 
172  SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), std::addressof(descriptor), false};
173  std::unique_ptr<void, close_handle> file{
174  CreateFile(
175  name.c_str(),
176  GENERIC_WRITE, FILE_SHARE_READ,
177  std::addressof(attributes),
178  CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
179  nullptr
180  )
181  };
182  if (file)
183  {
184  const int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file.get()), 0);
185  if (0 <= fd)
186  {
187  file.release();
188  std::FILE* real_file = _fdopen(fd, "w");
189  if (!real_file)
190  {
191  _close(fd);
192  }
193  return {real_file, std::move(name)};
194  }
195  }
196 #else
197  const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR);
198  if (0 <= fdr)
199  {
200  struct stat rstats = {};
201  if (fstat(fdr, std::addressof(rstats)) != 0)
202  {
203  close(fdr);
204  return {};
205  }
206  fchmod(fdr, (S_IRUSR | S_IWUSR));
207  const int fdw = open(name.c_str(), O_RDWR);
208  fchmod(fdr, rstats.st_mode);
209  close(fdr);
210 
211  if (0 <= fdw)
212  {
213  struct stat wstats = {};
214  if (fstat(fdw, std::addressof(wstats)) == 0 &&
215  rstats.st_dev == wstats.st_dev && rstats.st_ino == wstats.st_ino &&
216  flock_exnb(fdw) == 0 && ftruncate(fdw, 0) == 0)
217  {
218  std::FILE* file = fdopen(fdw, "w");
219  if (file) return {file, std::move(name)};
220  }
221  close(fdw);
222  }
223  }
224 #endif
225  return {};
226  }
227 
228  private_file::~private_file() noexcept
229  {
230  try
231  {
232  boost::system::error_code ec{};
233  boost::filesystem::remove(filename(), ec);
234  }
235  catch (...) {}
236  }
237 
238  file_locker::file_locker(const std::string &filename)
239  {
240 #ifdef WIN32
241  m_fd = INVALID_HANDLE_VALUE;
242  std::wstring filename_wide;
243  try
244  {
245  filename_wide = string_tools::utf8_to_utf16(filename);
246  }
247  catch (const std::exception &e)
248  {
249  MERROR("Failed to convert path \"" << filename << "\" to UTF-16: " << e.what());
250  return;
251  }
252  m_fd = CreateFileW(filename_wide.c_str(), GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
253  if (m_fd != INVALID_HANDLE_VALUE)
254  {
255  OVERLAPPED ov;
256  memset(&ov, 0, sizeof(ov));
257  if (!LockFileEx(m_fd, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ov))
258  {
259  MERROR("Failed to lock " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
260  CloseHandle(m_fd);
261  m_fd = INVALID_HANDLE_VALUE;
262  }
263  }
264  else
265  {
266  MERROR("Failed to open " << filename << ": " << std::error_code(GetLastError(), std::system_category()));
267  }
268 #else
269  m_fd = open(filename.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0666);
270  if (m_fd != -1)
271  {
272  if (flock_exnb(m_fd) == -1)
273  {
274  MERROR("Failed to lock " << filename << ": " << std::strerror(errno));
275  close(m_fd);
276  m_fd = -1;
277  }
278  }
279  else
280  {
281  MERROR("Failed to open " << filename << ": " << std::strerror(errno));
282  }
283 #endif
284  }
285  file_locker::~file_locker()
286  {
287  if (locked())
288  {
289 #ifdef WIN32
290  CloseHandle(m_fd);
291 #else
292  close(m_fd);
293 #endif
294  }
295  }
296  bool file_locker::locked() const
297  {
298 #ifdef WIN32
299  return m_fd != INVALID_HANDLE_VALUE;
300 #else
301  return m_fd != -1;
302 #endif
303  }
304 
305 #ifdef WIN32
306  std::string get_windows_version_display_string()
307  {
308  typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
309  typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
310 #define BUFSIZE 10000
311 
312  char pszOS[BUFSIZE] = {0};
313  OSVERSIONINFOEX osvi;
314  SYSTEM_INFO si;
315  PGNSI pGNSI;
316  PGPI pGPI;
317  BOOL bOsVersionInfoEx;
318  DWORD dwType;
319 
320  ZeroMemory(&si, sizeof(SYSTEM_INFO));
321  ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
322 
323  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
324  bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi);
325 
326  if(!bOsVersionInfoEx) return pszOS;
327 
328  // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
329 
330  pGNSI = (PGNSI) GetProcAddress(
331  GetModuleHandle(TEXT("kernel32.dll")),
332  "GetNativeSystemInfo");
333  if(NULL != pGNSI)
334  pGNSI(&si);
335  else GetSystemInfo(&si);
336 
337  if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId &&
338  osvi.dwMajorVersion > 4 )
339  {
340  StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft "));
341 
342  // Test for the specific product.
343  if ( osvi.dwMajorVersion == 10 )
344  {
345  if ( osvi.dwMinorVersion == 0 )
346  {
347  if( osvi.wProductType == VER_NT_WORKSTATION )
348  StringCchCat(pszOS, BUFSIZE, TEXT("Windows 10 "));
349  else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2016 " ));
350  }
351  }
352 
353  if ( osvi.dwMajorVersion == 6 )
354  {
355  if ( osvi.dwMinorVersion == 0 )
356  {
357  if( osvi.wProductType == VER_NT_WORKSTATION )
358  StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista "));
359  else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 " ));
360  }
361 
362  if ( osvi.dwMinorVersion == 1 )
363  {
364  if( osvi.wProductType == VER_NT_WORKSTATION )
365  StringCchCat(pszOS, BUFSIZE, TEXT("Windows 7 "));
366  else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " ));
367  }
368 
369  if ( osvi.dwMinorVersion == 2 )
370  {
371  if( osvi.wProductType == VER_NT_WORKSTATION )
372  StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8 "));
373  else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 " ));
374  }
375 
376  if ( osvi.dwMinorVersion == 3 )
377  {
378  if( osvi.wProductType == VER_NT_WORKSTATION )
379  StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8.1 "));
380  else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 R2 " ));
381  }
382 
383  pGPI = (PGPI) GetProcAddress(
384  GetModuleHandle(TEXT("kernel32.dll")),
385  "GetProductInfo");
386 
387  pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType);
388 
389  switch( dwType )
390  {
391  case PRODUCT_ULTIMATE:
392  StringCchCat(pszOS, BUFSIZE, TEXT("Ultimate Edition" ));
393  break;
394  case PRODUCT_PROFESSIONAL:
395  StringCchCat(pszOS, BUFSIZE, TEXT("Professional" ));
396  break;
397  case PRODUCT_HOME_PREMIUM:
398  StringCchCat(pszOS, BUFSIZE, TEXT("Home Premium Edition" ));
399  break;
400  case PRODUCT_HOME_BASIC:
401  StringCchCat(pszOS, BUFSIZE, TEXT("Home Basic Edition" ));
402  break;
403  case PRODUCT_ENTERPRISE:
404  StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
405  break;
406  case PRODUCT_BUSINESS:
407  StringCchCat(pszOS, BUFSIZE, TEXT("Business Edition" ));
408  break;
409  case PRODUCT_STARTER:
410  StringCchCat(pszOS, BUFSIZE, TEXT("Starter Edition" ));
411  break;
412  case PRODUCT_CLUSTER_SERVER:
413  StringCchCat(pszOS, BUFSIZE, TEXT("Cluster Server Edition" ));
414  break;
415  case PRODUCT_DATACENTER_SERVER:
416  StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition" ));
417  break;
418  case PRODUCT_DATACENTER_SERVER_CORE:
419  StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition (core installation)" ));
420  break;
421  case PRODUCT_ENTERPRISE_SERVER:
422  StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" ));
423  break;
424  case PRODUCT_ENTERPRISE_SERVER_CORE:
425  StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition (core installation)" ));
426  break;
427  case PRODUCT_ENTERPRISE_SERVER_IA64:
428  StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition for Itanium-based Systems" ));
429  break;
430  case PRODUCT_SMALLBUSINESS_SERVER:
431  StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server" ));
432  break;
433  case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
434  StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server Premium Edition" ));
435  break;
436  case PRODUCT_STANDARD_SERVER:
437  StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition" ));
438  break;
439  case PRODUCT_STANDARD_SERVER_CORE:
440  StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition (core installation)" ));
441  break;
442  case PRODUCT_WEB_SERVER:
443  StringCchCat(pszOS, BUFSIZE, TEXT("Web Server Edition" ));
444  break;
445  }
446  }
447 
448  if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
449  {
450  if( GetSystemMetrics(SM_SERVERR2) )
451  StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Server 2003 R2, "));
452  else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER )
453  StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Storage Server 2003"));
454  else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER )
455  StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Home Server"));
456  else if( osvi.wProductType == VER_NT_WORKSTATION &&
457  si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
458  {
459  StringCchCat(pszOS, BUFSIZE, TEXT( "Windows XP Professional x64 Edition"));
460  }
461  else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2003, "));
462 
463  // Test for the server type.
464  if ( osvi.wProductType != VER_NT_WORKSTATION )
465  {
466  if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 )
467  {
468  if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
469  StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition for Itanium-based Systems" ));
470  else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
471  StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition for Itanium-based Systems" ));
472  }
473 
474  else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
475  {
476  if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
477  StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter x64 Edition" ));
478  else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
479  StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise x64 Edition" ));
480  else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard x64 Edition" ));
481  }
482 
483  else
484  {
485  if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
486  StringCchCat(pszOS, BUFSIZE, TEXT( "Compute Cluster Edition" ));
487  else if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
488  StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition" ));
489  else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
490  StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition" ));
491  else if ( osvi.wSuiteMask & VER_SUITE_BLADE )
492  StringCchCat(pszOS, BUFSIZE, TEXT( "Web Edition" ));
493  else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard Edition" ));
494  }
495  }
496  }
497 
498  if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
499  {
500  StringCchCat(pszOS, BUFSIZE, TEXT("Windows XP "));
501  if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
502  StringCchCat(pszOS, BUFSIZE, TEXT( "Home Edition" ));
503  else StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
504  }
505 
506  if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
507  {
508  StringCchCat(pszOS, BUFSIZE, TEXT("Windows 2000 "));
509 
510  if ( osvi.wProductType == VER_NT_WORKSTATION )
511  {
512  StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" ));
513  }
514  else
515  {
516  if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
517  StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Server" ));
518  else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
519  StringCchCat(pszOS, BUFSIZE, TEXT( "Advanced Server" ));
520  else StringCchCat(pszOS, BUFSIZE, TEXT( "Server" ));
521  }
522  }
523 
524  // Include service pack (if any) and build number.
525 
526  if( strlen(osvi.szCSDVersion) > 0 )
527  {
528  StringCchCat(pszOS, BUFSIZE, TEXT(" ") );
529  StringCchCat(pszOS, BUFSIZE, osvi.szCSDVersion);
530  }
531 
532  TCHAR buf[80];
533 
534  StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber);
535  StringCchCat(pszOS, BUFSIZE, buf);
536 
537  if ( osvi.dwMajorVersion >= 6 )
538  {
539  if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 )
540  StringCchCat(pszOS, BUFSIZE, TEXT( ", 64-bit" ));
541  else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL )
542  StringCchCat(pszOS, BUFSIZE, TEXT(", 32-bit"));
543  }
544 
545  return pszOS;
546  }
547  else
548  {
549  printf( "This sample does not support this version of Windows.\n");
550  return pszOS;
551  }
552  }
553 #else
555 {
556  struct utsname un;
557 
558  if(uname(&un) < 0)
559  return std::string("*nix: failed to get os version");
560  return std::string() + un.sysname + " " + un.version + " " + un.release;
561 }
562 #endif
563 
564 
565 
567  {
568 #ifdef WIN32
569  return get_windows_version_display_string();
570 #else
572 #endif
573  }
574 
575 
576 
577 #ifdef WIN32
578  std::string get_special_folder_path(int nfolder, bool iscreate)
579  {
580  WCHAR psz_path[MAX_PATH] = L"";
581 
582  if (SHGetSpecialFolderPathW(NULL, psz_path, nfolder, iscreate))
583  {
584  try
585  {
586  return string_tools::utf16_to_utf8(psz_path);
587  }
588  catch (const std::exception &e)
589  {
590  MERROR("utf16_to_utf8 failed: " << e.what());
591  return "";
592  }
593  }
594 
595  LOG_ERROR("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
596  return "";
597  }
598 #endif
599 
601  {
602  /* Please for the love of god refactor the ifdefs out of this */
603 
604  // namespace fs = boost::filesystem;
605  // Windows < Vista: C:\Documents and Settings\Username\Application Data\CRYPTONOTE_NAME
606  // Windows >= Vista: C:\Users\Username\AppData\Roaming\CRYPTONOTE_NAME
607  // Unix & Mac: ~/.CRYPTONOTE_NAME
608  std::string config_folder;
609 
610 #ifdef WIN32
611  config_folder = get_special_folder_path(CSIDL_COMMON_APPDATA, true) + "\\" + CRYPTONOTE_NAME;
612 #else
613  std::string pathRet;
614  char* pszHome = getenv("HOME");
615  if (pszHome == NULL || strlen(pszHome) == 0)
616  pathRet = "/";
617  else
618  pathRet = pszHome;
619  config_folder = (pathRet + "/." + CRYPTONOTE_NAME);
620 #endif
621 
622  return config_folder;
623  }
624 
626  {
627  namespace fs = boost::filesystem;
628  boost::system::error_code ec;
629  fs::path fs_path(path);
630  if (fs::is_directory(fs_path, ec))
631  {
632  return true;
633  }
634 
635  bool res = fs::create_directories(fs_path, ec);
636  if (res)
637  {
638  LOG_PRINT_L2("Created directory: " << path);
639  }
640  else
641  {
642  LOG_PRINT_L2("Can't create directory: " << path << ", err: "<< ec.message());
643  }
644 
645  return res;
646  }
647 
648  std::error_code replace_file(const std::string& old_name, const std::string& new_name)
649  {
650  int code;
651 #if defined(WIN32)
652  // Maximizing chances for success
653  std::wstring wide_replacement_name;
654  try { wide_replacement_name = string_tools::utf8_to_utf16(old_name); }
655  catch (...) { return std::error_code(GetLastError(), std::system_category()); }
656  std::wstring wide_replaced_name;
657  try { wide_replaced_name = string_tools::utf8_to_utf16(new_name); }
658  catch (...) { return std::error_code(GetLastError(), std::system_category()); }
659 
660  DWORD attributes = ::GetFileAttributesW(wide_replaced_name.c_str());
661  if (INVALID_FILE_ATTRIBUTES != attributes)
662  {
663  ::SetFileAttributesW(wide_replaced_name.c_str(), attributes & (~FILE_ATTRIBUTE_READONLY));
664  }
665 
666  bool ok = 0 != ::MoveFileExW(wide_replacement_name.c_str(), wide_replaced_name.c_str(), MOVEFILE_REPLACE_EXISTING);
667  code = ok ? 0 : static_cast<int>(::GetLastError());
668 #else
669  bool ok = 0 == std::rename(old_name.c_str(), new_name.c_str());
670  code = ok ? 0 : errno;
671 #endif
672  return std::error_code(code, std::system_category());
673  }
674 
675  static bool unbound_built_with_threads()
676  {
677  ub_ctx *ctx = ub_ctx_create();
678  if (!ctx) return false; // cheat a bit, should not happen unless OOM
679  char *electroneum = strdup("electroneum"), *unbound = strdup("unbound");
680  ub_ctx_zone_add(ctx, electroneum, unbound); // this calls ub_ctx_finalize first, then errors out with UB_SYNTAX
681  free(unbound);
682  free(electroneum);
683  // if no threads, bails out early with UB_NOERROR, otherwise fails with UB_AFTERFINAL id already finalized
684  bool with_threads = ub_ctx_async(ctx, 1) != 0; // UB_AFTERFINAL is not defined in public headers, check any error
685  ub_ctx_delete(ctx);
686  MINFO("libunbound was built " << (with_threads ? "with" : "without") << " threads");
687  return with_threads;
688  }
689 
691  {
692  // boost::filesystem throws for "invalid" locales, such as en_US.UTF-8, or kjsdkfs,
693  // so reset it here before any calls to it
694  try
695  {
696  boost::filesystem::path p {std::string("test")};
697  p /= std::string("test");
698  }
699  catch (...)
700  {
701 #if defined(__MINGW32__) || defined(__MINGW__)
702  putenv("LC_ALL=C");
703  putenv("LANG=C");
704 #else
705  setenv("LC_ALL", "C", 1);
706  setenv("LANG", "C", 1);
707 #endif
708  return true;
709  }
710  return false;
711  }
712 
713 #ifdef STACK_TRACE
714 #ifdef _WIN32
715  // https://stackoverflow.com/questions/1992816/how-to-handle-seg-faults-under-windows
716  static LONG WINAPI windows_crash_handler(PEXCEPTION_POINTERS pExceptionInfo)
717  {
718  tools::log_stack_trace("crashing");
719  exit(1);
720  return EXCEPTION_CONTINUE_SEARCH;
721  }
722  static void setup_crash_dump()
723  {
724  SetUnhandledExceptionFilter(windows_crash_handler);
725  }
726 #else
727  static void posix_crash_handler(int signal)
728  {
729  tools::log_stack_trace(("crashing with fatal signal " + std::to_string(signal)).c_str());
730 #ifdef NDEBUG
731  _exit(1);
732 #else
733  abort();
734 #endif
735  }
736  static void setup_crash_dump()
737  {
738  signal(SIGSEGV, posix_crash_handler);
739  signal(SIGBUS, posix_crash_handler);
740  signal(SIGILL, posix_crash_handler);
741  signal(SIGFPE, posix_crash_handler);
742  }
743 #endif
744 #else
745  static void setup_crash_dump() {}
746 #endif
747 
749  {
750 #ifdef __GLIBC__
751  // disable core dumps in release mode
752  struct rlimit rlimit;
753  rlimit.rlim_cur = rlimit.rlim_max = 0;
754  if (setrlimit(RLIMIT_CORE, &rlimit))
755  {
756  MWARNING("Failed to disable core dumps");
757  return false;
758  }
759 #endif
760  return true;
761  }
762 
764  {
765 #ifdef __GLIBC__
766  struct rlimit rlim;
767  if (getrlimit(RLIMIT_MEMLOCK, &rlim) < 0)
768  {
769  MERROR("Failed to determine the lockable memory limit");
770  return -1;
771  }
772  return rlim.rlim_cur;
773 #else
774  return -1;
775 #endif
776  }
777 
778  bool on_startup()
779  {
780  mlog_configure("", true);
781 
782  setup_crash_dump();
783 
784  sanitize_locale();
785 
786 #ifdef __GLIBC__
787  const char *ver = gnu_get_libc_version();
788  if (!strcmp(ver, "2.25"))
789  MCLOG_RED(el::Level::Warning, "global", "Running with glibc " << ver << ", hangs may occur - change glibc version if possible");
790 #endif
791 
792 #if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(LIBRESSL_VERSION_TEXT)
793  SSL_library_init();
794 #else
795  OPENSSL_init_ssl(0, NULL);
796 #endif
797 
798  if (!unbound_built_with_threads())
799  MCLOG_RED(el::Level::Warning, "global", "libunbound was not built with threads enabled - crashes may occur");
800 
801  return true;
802  }
804  {
805 #if defined(__MINGW32__) || defined(__MINGW__)
806  // no clue about the odd one out
807 #else
808  mode_t mode = strict ? 077 : 0;
809  umask(mode);
810 #endif
811  }
812 
813  boost::optional<bool> is_hdd(const char *file_path)
814  {
815 #ifdef __GLIBC__
816  struct stat st;
817  std::string prefix;
818  if(stat(file_path, &st) == 0)
819  {
820  std::ostringstream s;
821  s << "/sys/dev/block/" << major(st.st_dev) << ":" << minor(st.st_dev);
822  prefix = s.str();
823  }
824  else
825  {
826  return boost::none;
827  }
828  std::string attr_path = prefix + "/queue/rotational";
829  std::ifstream f(attr_path, std::ios_base::in);
830  if(not f.is_open())
831  {
832  attr_path = prefix + "/../queue/rotational";
833  f.open(attr_path, std::ios_base::in);
834  if(not f.is_open())
835  {
836  return boost::none;
837  }
838  }
839  unsigned short val = 0xdead;
840  f >> val;
841  if(not f.fail())
842  {
843  return (val == 1);
844  }
845  return boost::none;
846 #else
847  return boost::none;
848 #endif
849  }
850 
851  namespace
852  {
853  boost::mutex max_concurrency_lock;
854  unsigned max_concurrency = boost::thread::hardware_concurrency();
855  }
856 
857  void set_max_concurrency(unsigned n)
858  {
859  if (n < 1)
860  n = boost::thread::hardware_concurrency();
861  unsigned hwc = boost::thread::hardware_concurrency();
862  if (n > hwc)
863  n = hwc;
864  boost::lock_guard<boost::mutex> lock(max_concurrency_lock);
865  max_concurrency = n;
866  }
867 
869  {
870  boost::lock_guard<boost::mutex> lock(max_concurrency_lock);
871  return max_concurrency;
872  }
873 
875  {
876  // always assume Tor/I2P addresses to be untrusted by default
877  if (boost::ends_with(address, ".onion") || boost::ends_with(address, ".i2p"))
878  {
879  MDEBUG("Address '" << address << "' is Tor/I2P, non local");
880  return false;
881  }
882 
883  // extract host
886  {
887  MWARNING("Failed to determine whether address '" << address << "' is local, assuming not");
888  return false;
889  }
890  if (u_c.host.empty())
891  {
892  MWARNING("Failed to determine whether address '" << address << "' is local, assuming not");
893  return false;
894  }
895 
896  // resolve to IP
897  boost::asio::io_service io_service;
898  boost::asio::ip::tcp::resolver resolver(io_service);
899  boost::asio::ip::tcp::resolver::query query(u_c.host, "");
900  boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query);
901  while (i != boost::asio::ip::tcp::resolver::iterator())
902  {
903  const boost::asio::ip::tcp::endpoint &ep = *i;
904  if (ep.address().is_loopback())
905  {
906  MDEBUG("Address '" << address << "' is local");
907  return true;
908  }
909  ++i;
910  }
911 
912  MDEBUG("Address '" << address << "' is not local");
913  return false;
914  }
915  int vercmp(const char *v0, const char *v1)
916  {
917  std::vector<std::string> f0, f1;
918  boost::split(f0, v0, boost::is_any_of(".-"));
919  boost::split(f1, v1, boost::is_any_of(".-"));
920  for (size_t i = 0; i < std::max(f0.size(), f1.size()); ++i) {
921  if (i >= f0.size())
922  return -1;
923  if (i >= f1.size())
924  return 1;
925  int f0i = atoi(f0[i].c_str()), f1i = atoi(f1[i].c_str());
926  int n = f0i - f1i;
927  if (n)
928  return n;
929  }
930  return 0;
931  }
932 
933  bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash)
934  {
935  SHA256_CTX ctx;
936  if (!SHA256_Init(&ctx))
937  return false;
938  if (!SHA256_Update(&ctx, data, len))
939  return false;
940  if (!SHA256_Final((unsigned char*)hash.data, &ctx))
941  return false;
942  return true;
943  }
944 
945  bool sha256sum(const std::string &filename, crypto::hash &hash)
946  {
947  if (!epee::file_io_utils::is_file_exist(filename))
948  return false;
949  std::ifstream f;
950  f.exceptions(std::ifstream::failbit | std::ifstream::badbit);
951  f.open(filename, std::ios_base::binary | std::ios_base::in | std::ios::ate);
952  if (!f)
953  return false;
954  std::ifstream::pos_type file_size = f.tellg();
955  SHA256_CTX ctx;
956  if (!SHA256_Init(&ctx))
957  return false;
958  size_t size_left = file_size;
959  f.seekg(0, std::ios::beg);
960  while (size_left)
961  {
962  char buf[4096];
963  std::ifstream::pos_type read_size = size_left > sizeof(buf) ? sizeof(buf) : size_left;
964  f.read(buf, read_size);
965  if (!f || !f.good())
966  return false;
967  if (!SHA256_Update(&ctx, buf, read_size))
968  return false;
969  size_left -= read_size;
970  }
971  f.close();
972  if (!SHA256_Final((unsigned char*)hash.data, &ctx))
973  return false;
974  return true;
975  }
976 
977  boost::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str)
978  {
979  auto pos = str.find(":");
980  bool r = pos != std::string::npos;
981  uint32_t major;
982  r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos));
983  uint32_t minor;
984  r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1));
985  if (r)
986  {
987  return std::make_pair(major, minor);
988  }
989  else
990  {
991  return {};
992  }
993  }
994 
996  std::string s;
997  x++;
998  if (x == 1) {
999  s = "|";
1000  } else if (x == 2) {
1001  s = "/";
1002  } else if (x == 3) {
1003  s = "-";
1004  } else {
1005  s = "\\";
1006  x = 0;
1007  }
1008  std::cout << "\r" << s << std::flush;
1009  return x;
1010  }
1011 
1013  {
1014  std::string newval;
1015 
1016  bool escape = false;
1017  for (char c: val)
1018  {
1019  if (c == '*')
1020  newval += escape ? "*" : ".*";
1021  else if (c == '?')
1022  newval += escape ? "?" : ".";
1023  else if (c == '\\')
1024  newval += '\\', escape = !escape;
1025  else
1026  newval += c;
1027  }
1028  return newval;
1029  }
1030 
1031 #ifdef _WIN32
1032  std::string input_line_win()
1033  {
1034  HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
1035  DWORD oldMode;
1036 
1037  FlushConsoleInputBuffer(hConIn);
1038  GetConsoleMode(hConIn, &oldMode);
1039  SetConsoleMode(hConIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
1040 
1041  wchar_t buffer[1024];
1042  DWORD read;
1043 
1044  ReadConsoleW(hConIn, buffer, sizeof(buffer)/sizeof(wchar_t)-1, &read, nullptr);
1045  buffer[read] = 0;
1046 
1047  SetConsoleMode(hConIn, oldMode);
1048  CloseHandle(hConIn);
1049 
1050  int size_needed = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
1051  std::string buf(size_needed, '\0');
1052  WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &buf[0], size_needed, NULL, NULL);
1053  buf.pop_back(); //size_needed includes null that we needed to have space for
1054  return buf;
1055  }
1056 #endif
1057 
1058  void closefrom(int fd)
1059  {
1060 #if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ || defined __DragonFly__
1061  ::closefrom(fd);
1062 #else
1063 #if defined __GLIBC__
1064  const int sc_open_max = sysconf(_SC_OPEN_MAX);
1065  const int MAX_FDS = std::min(65536, sc_open_max);
1066 #else
1067  const int MAX_FDS = 65536;
1068 #endif
1069  while (fd < MAX_FDS)
1070  {
1071  close(fd);
1072  ++fd;
1073  }
1074 #endif
1075  }
1076 
1078  {
1079  char buffer[64];
1080  if (ts < 1234567890)
1081  return "<unknown>";
1082  time_t tt = ts;
1083  struct tm tm;
1084  misc_utils::get_gmt_time(tt, tm);
1085  strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm);
1086  return std::string(buffer);
1087  }
1088 
1090  {
1091  // Use 1024 for "kilo", 1024*1024 for "mega" and so on instead of the more modern and standard-conforming
1092  // 1000, 1000*1000 and so on, to be consistent with other Electroneum code that also uses base 2 units
1093  struct byte_map
1094  {
1095  const char* const format;
1096  const std::uint64_t bytes;
1097  };
1098 
1099  static constexpr const byte_map sizes[] =
1100  {
1101  {"%.0f B", 1024},
1102  {"%.2f KB", 1024 * 1024},
1103  {"%.2f MB", std::uint64_t(1024) * 1024 * 1024},
1104  {"%.2f GB", std::uint64_t(1024) * 1024 * 1024 * 1024},
1105  {"%.2f TB", std::uint64_t(1024) * 1024 * 1024 * 1024 * 1024}
1106  };
1107 
1108  struct bytes_less
1109  {
1110  bool operator()(const byte_map& lhs, const byte_map& rhs) const noexcept
1111  {
1112  return lhs.bytes < rhs.bytes;
1113  }
1114  };
1115 
1116  const auto size = std::upper_bound(
1117  std::begin(sizes), std::end(sizes) - 1, byte_map{"", bytes}, bytes_less{}
1118  );
1119  const std::uint64_t divisor = size->bytes / 1024;
1120  return (boost::format(size->format) % (double(bytes) / divisor)).str();
1121  }
1122 
1123 }
const char * res
Definition: hmac_keccak.cpp:41
bool sha256sum(const std::string &filename, crypto::hash &hash)
Definition: util.cpp:945
#define MERROR(x)
Definition: misc_log_ex.h:73
int display_simple_progress_spinner(int x)
Definition: util.cpp:995
#define LOG_PRINT_L2(x)
Definition: misc_log_ex.h:101
#define MINFO(x)
Definition: misc_log_ex.h:75
::std::string string
Definition: gtest-port.h:1097
bool is_local_address(const std::string &address)
Definition: util.cpp:874
std::string get_os_version_string()
Returns the OS version string.
Definition: util.cpp:566
void set_max_concurrency(unsigned n)
Definition: util.cpp:857
void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size=MAX_LOG_FILE_SIZE, const std::size_t max_log_files=MAX_LOG_FILES)
Definition: mlog.cpp:148
bool disable_core_dumps()
Definition: util.cpp:748
std::string glob_to_regex(const std::string &val)
Definition: util.cpp:1012
#define MCLOG_RED(level, cat, x)
Definition: misc_log_ex.h:58
bool is_file_exist(const std::string &path)
Definition: file_io_utils.h:66
unsigned char uint8_t
Definition: stdint.h:124
const char * name
std::string get_nix_version_display_string()
Definition: util.cpp:554
#define MDEBUG(x)
Definition: misc_log_ex.h:76
unsigned get_max_concurrency()
Definition: util.cpp:868
std::string get_default_data_dir()
Returns the default data directory.
Definition: util.cpp:600
bool on_startup()
Definition: util.cpp:778
#define CRYPTONOTE_NAME
unsigned int uint32_t
Definition: stdint.h:126
::std::wstring wstring
Definition: gtest-port.h:1103
Various Tools.
Definition: tools.cpp:31
struct ub_ctx * ub_ctx_create(void)
bool get_gmt_time(time_t t, struct tm &tm)
unsigned __int64 uint64_t
Definition: stdint.h:136
std::error_code replace_file(const std::string &old_name, const std::string &new_name)
std::rename wrapper for nix and something strange for windows.
Definition: util.cpp:648
const char * buf
Definition: slow_memmem.cpp:74
Useful when application has potentially harmful situtaions.
#define MWARNING(x)
Definition: misc_log_ex.h:74
int ub_ctx_zone_add(struct ub_ctx *ctx, const char *zone_name, const char *zone_type)
boost::optional< std::pair< uint32_t, uint32_t > > parse_subaddress_lookahead(const std::string &str)
Definition: util.cpp:977
const T & move(const T &t)
Definition: gtest-port.h:1317
bool parse_url(const std::string url_str, http::url_content &content)
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
void set_strict_default_file_permissions(bool strict)
Definition: util.cpp:803
void ub_ctx_delete(struct ub_ctx *ctx)
bool sanitize_locale()
Definition: util.cpp:690
bool create_directories_if_necessary(const std::string &path)
creates directories for a path
Definition: util.cpp:625
POD_CLASS hash
Definition: hash.h:50
std::string get_human_readable_bytes(uint64_t bytes)
Definition: util.cpp:1089
const T1 & f1
Definition: gtest-tuple.h:683
int ub_ctx_async(struct ub_ctx *ctx, int dothread)
const char * address
Definition: multisig.cpp:37
std::string to_string(t_connection_type type)
void closefrom(int fd)
Definition: util.cpp:1058
void log_stack_trace(const char *msg)
A file restricted to process owner AND process. Deletes file on destruction.
Definition: util.h:71
PUSH_WARNINGS bool get_xtype_from_string(OUT XType &val, const std::string &str_id)
Definition: string_tools.h:125
ssize_t get_lockable_memory()
Definition: util.cpp:763
std::string get_human_readable_timestamp(uint64_t ts)
Definition: util.cpp:1077
Definition: context.h:62
int vercmp(const char *v0, const char *v1)
Definition: util.cpp:915
boost::optional< bool > is_hdd(const char *file_path)
Definition: util.cpp:813