execpath.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #include "platform.h"
  2. #if defined(_solaris_)
  3. #include <stdlib.h>
  4. #elif defined(_darwin_)
  5. #include <mach-o/dyld.h>
  6. #include <util/generic/function.h>
  7. #elif defined(_win_)
  8. #include "winint.h"
  9. #include <io.h>
  10. #elif defined(_linux_)
  11. #elif defined(_freebsd_)
  12. #include <string.h>
  13. #include <sys/types.h> // for u_int not defined in sysctl.h
  14. #include <sys/sysctl.h>
  15. #include <unistd.h>
  16. #endif
  17. #include <util/generic/singleton.h>
  18. #include "execpath.h"
  19. #include "fs.h"
  20. #if defined(_freebsd_)
  21. static inline bool GoodPath(const TString& path) {
  22. return path.find('/') != TString::npos;
  23. }
  24. static inline int FreeBSDSysCtl(int* mib, size_t mibSize, TTempBuf& res) {
  25. for (size_t i = 0; i < 2; ++i) {
  26. size_t cb = res.Size();
  27. if (sysctl(mib, mibSize, res.Data(), &cb, nullptr, 0) == 0) {
  28. res.Proceed(cb);
  29. return 0;
  30. } else if (errno == ENOMEM) {
  31. res = TTempBuf(cb);
  32. } else {
  33. return errno;
  34. }
  35. }
  36. return errno;
  37. }
  38. static inline TString FreeBSDGetExecPath() {
  39. int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
  40. TTempBuf buf;
  41. int r = FreeBSDSysCtl(mib, Y_ARRAY_SIZE(mib), buf);
  42. if (r == 0) {
  43. return TString(buf.Data(), buf.Filled() - 1);
  44. } else if (r == ENOTSUP) { // older FreeBSD version
  45. /*
  46. * BSD analogue for /proc/self is /proc/curproc.
  47. * See:
  48. * https://www.freebsd.org/cgi/man.cgi?query=procfs&sektion=5&format=html
  49. */
  50. TString path("/proc/curproc/file");
  51. return NFs::ReadLink(path);
  52. } else {
  53. return TString();
  54. }
  55. }
  56. static inline TString FreeBSDGetArgv0() {
  57. int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, getpid()};
  58. TTempBuf buf;
  59. int r = FreeBSDSysCtl(mib, Y_ARRAY_SIZE(mib), buf);
  60. if (r == 0) {
  61. return TString(buf.Data());
  62. } else if (r == ENOTSUP) {
  63. return TString();
  64. } else {
  65. ythrow yexception() << "FreeBSDGetArgv0() failed: " << LastSystemErrorText();
  66. }
  67. }
  68. static inline bool FreeBSDGuessExecPath(const TString& guessPath, TString& execPath) {
  69. if (NFs::Exists(guessPath)) {
  70. // now it should work for real
  71. execPath = FreeBSDGetExecPath();
  72. if (RealPath(execPath) == RealPath(guessPath)) {
  73. return true;
  74. }
  75. }
  76. return false;
  77. }
  78. static inline bool FreeBSDGuessExecBasePath(const TString& guessBasePath, TString& execPath) {
  79. return FreeBSDGuessExecPath(TString(guessBasePath) + "/" + getprogname(), execPath);
  80. }
  81. #endif
  82. static TString GetExecPathImpl() {
  83. #if defined(_solaris_)
  84. return execname();
  85. #elif defined(_darwin_)
  86. TTempBuf execNameBuf;
  87. for (size_t i = 0; i < 2; ++i) {
  88. std::remove_pointer_t<TFunctionArg<decltype(_NSGetExecutablePath), 1>> bufsize = execNameBuf.Size();
  89. int r = _NSGetExecutablePath(execNameBuf.Data(), &bufsize);
  90. if (r == 0) {
  91. return execNameBuf.Data();
  92. } else if (r == -1) {
  93. execNameBuf = TTempBuf(bufsize);
  94. }
  95. }
  96. ythrow yexception() << "GetExecPathImpl() failed";
  97. #elif defined(_win_)
  98. TTempBuf execNameBuf;
  99. for (;;) {
  100. DWORD r = GetModuleFileName(nullptr, execNameBuf.Data(), execNameBuf.Size());
  101. if (r == execNameBuf.Size()) {
  102. execNameBuf = TTempBuf(execNameBuf.Size() * 2);
  103. } else if (r == 0) {
  104. ythrow yexception() << "GetExecPathImpl() failed: " << LastSystemErrorText();
  105. } else {
  106. return execNameBuf.Data();
  107. }
  108. }
  109. #elif defined(_linux_) || defined(_cygwin_)
  110. TString path("/proc/self/exe");
  111. return NFs::ReadLink(path);
  112. // TODO(yoda): check if the filename ends with " (deleted)"
  113. #elif defined(_freebsd_)
  114. TString execPath = FreeBSDGetExecPath();
  115. if (GoodPath(execPath)) {
  116. return execPath;
  117. }
  118. if (FreeBSDGuessExecPath(FreeBSDGetArgv0(), execPath)) {
  119. return execPath;
  120. }
  121. if (FreeBSDGuessExecPath(getenv("_"), execPath)) {
  122. return execPath;
  123. }
  124. if (FreeBSDGuessExecBasePath(getenv("PWD"), execPath)) {
  125. return execPath;
  126. }
  127. if (FreeBSDGuessExecBasePath(NFs::CurrentWorkingDirectory(), execPath)) {
  128. return execPath;
  129. }
  130. ythrow yexception() << "can not resolve exec path";
  131. #else
  132. #error dont know how to implement GetExecPath on this platform
  133. #endif
  134. }
  135. static bool GetPersistentExecPathImpl(TString& to) {
  136. #if defined(_solaris_)
  137. to = TString("/proc/self/object/a.out");
  138. return true;
  139. #elif defined(_linux_) || defined(_cygwin_)
  140. to = TString("/proc/self/exe");
  141. return true;
  142. #elif defined(_freebsd_)
  143. to = TString("/proc/curproc/file");
  144. return true;
  145. #else // defined(_win_) || defined(_darwin_) or unknown
  146. Y_UNUSED(to);
  147. return false;
  148. #endif
  149. }
  150. namespace {
  151. struct TExecPathsHolder {
  152. inline TExecPathsHolder() {
  153. ExecPath = GetExecPathImpl();
  154. if (!GetPersistentExecPathImpl(PersistentExecPath)) {
  155. PersistentExecPath = ExecPath;
  156. }
  157. }
  158. static inline auto Instance() {
  159. return SingletonWithPriority<TExecPathsHolder, 1>();
  160. }
  161. TString ExecPath;
  162. TString PersistentExecPath;
  163. };
  164. } // namespace
  165. const TString& GetExecPath() {
  166. return TExecPathsHolder::Instance()->ExecPath;
  167. }
  168. const TString& GetPersistentExecPath() {
  169. return TExecPathsHolder::Instance()->PersistentExecPath;
  170. }