diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/system/execpath.cpp | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/system/execpath.cpp')
-rw-r--r-- | util/system/execpath.cpp | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/util/system/execpath.cpp b/util/system/execpath.cpp new file mode 100644 index 0000000000..33198af58b --- /dev/null +++ b/util/system/execpath.cpp @@ -0,0 +1,199 @@ +#include "platform.h" + +#include <stdlib.h> + +#if defined(_solaris_) + #include <stdlib.h> +#elif defined(_darwin_) + #include <mach-o/dyld.h> +#elif defined(_win_) + #include "winint.h" + #include <io.h> +#elif defined(_linux_) + #include <unistd.h> +#elif defined(_freebsd_) + #include <string.h> + #include <sys/types.h> // for u_int not defined in sysctl.h + #include <sys/sysctl.h> + #include <unistd.h> +#endif + +#include <util/folder/dirut.h> +#include <util/generic/singleton.h> +#include <util/generic/function.h> +#include <util/generic/yexception.h> +#include <util/memory/tempbuf.h> +#include <util/stream/file.h> +#include <util/stream/pipe.h> +#include <util/string/cast.h> + +#include "filemap.h" +#include "execpath.h" +#include "fs.h" + +#if defined(_freebsd_) +static inline bool GoodPath(const TString& path) { + return path.find('/') != TString::npos; +} + +static inline int FreeBSDSysCtl(int* mib, size_t mibSize, TTempBuf& res) { + for (size_t i = 0; i < 2; ++i) { + size_t cb = res.Size(); + if (sysctl(mib, mibSize, res.Data(), &cb, nullptr, 0) == 0) { + res.Proceed(cb); + return 0; + } else if (errno == ENOMEM) { + res = TTempBuf(cb); + } else { + return errno; + } + } + return errno; +} + +static inline TString FreeBSDGetExecPath() { + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + TTempBuf buf; + int r = FreeBSDSysCtl(mib, Y_ARRAY_SIZE(mib), buf); + if (r == 0) { + return TString(buf.Data(), buf.Filled() - 1); + } else if (r == ENOTSUP) { // older FreeBSD version + /* + * BSD analogue for /proc/self is /proc/curproc. + * See: + * https://www.freebsd.org/cgi/man.cgi?query=procfs&sektion=5&format=html + */ + TString path("/proc/curproc/file"); + return NFs::ReadLink(path); + } else { + return TString(); + } +} + +static inline TString FreeBSDGetArgv0() { + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, getpid()}; + TTempBuf buf; + int r = FreeBSDSysCtl(mib, Y_ARRAY_SIZE(mib), buf); + if (r == 0) { + return TString(buf.Data()); + } else if (r == ENOTSUP) { + return TString(); + } else { + ythrow yexception() << "FreeBSDGetArgv0() failed: " << LastSystemErrorText(); + } +} + +static inline bool FreeBSDGuessExecPath(const TString& guessPath, TString& execPath) { + if (NFs::Exists(guessPath)) { + // now it should work for real + execPath = FreeBSDGetExecPath(); + if (RealPath(execPath) == RealPath(guessPath)) { + return true; + } + } + return false; +} + +static inline bool FreeBSDGuessExecBasePath(const TString& guessBasePath, TString& execPath) { + return FreeBSDGuessExecPath(TString(guessBasePath) + "/" + getprogname(), execPath); +} + +#endif + +static TString GetExecPathImpl() { +#if defined(_solaris_) + return execname(); +#elif defined(_darwin_) + TTempBuf execNameBuf; + for (size_t i = 0; i < 2; ++i) { + std::remove_pointer_t<TFunctionArg<decltype(_NSGetExecutablePath), 1>> bufsize = execNameBuf.Size(); + int r = _NSGetExecutablePath(execNameBuf.Data(), &bufsize); + if (r == 0) { + return execNameBuf.Data(); + } else if (r == -1) { + execNameBuf = TTempBuf(bufsize); + } + } + ythrow yexception() << "GetExecPathImpl() failed"; +#elif defined(_win_) + TTempBuf execNameBuf; + for (;;) { + DWORD r = GetModuleFileName(nullptr, execNameBuf.Data(), execNameBuf.Size()); + if (r == execNameBuf.Size()) { + execNameBuf = TTempBuf(execNameBuf.Size() * 2); + } else if (r == 0) { + ythrow yexception() << "GetExecPathImpl() failed: " << LastSystemErrorText(); + } else { + return execNameBuf.Data(); + } + } +#elif defined(_linux_) || defined(_cygwin_) + TString path("/proc/self/exe"); + return NFs::ReadLink(path); +// TODO(yoda): check if the filename ends with " (deleted)" +#elif defined(_freebsd_) + TString execPath = FreeBSDGetExecPath(); + if (GoodPath(execPath)) { + return execPath; + } + if (FreeBSDGuessExecPath(FreeBSDGetArgv0(), execPath)) { + return execPath; + } + if (FreeBSDGuessExecPath(getenv("_"), execPath)) { + return execPath; + } + if (FreeBSDGuessExecBasePath(getenv("PWD"), execPath)) { + return execPath; + } + if (FreeBSDGuessExecBasePath(NFs::CurrentWorkingDirectory(), execPath)) { + return execPath; + } + + ythrow yexception() << "can not resolve exec path"; +#else + #error dont know how to implement GetExecPath on this platform +#endif +} + +static bool GetPersistentExecPathImpl(TString& to) { +#if defined(_solaris_) + to = TString("/proc/self/object/a.out"); + return true; +#elif defined(_linux_) || defined(_cygwin_) + to = TString("/proc/self/exe"); + return true; +#elif defined(_freebsd_) + to = TString("/proc/curproc/file"); + return true; +#else // defined(_win_) || defined(_darwin_) or unknown + Y_UNUSED(to); + return false; +#endif +} + +namespace { + struct TExecPathsHolder { + inline TExecPathsHolder() { + ExecPath = GetExecPathImpl(); + + if (!GetPersistentExecPathImpl(PersistentExecPath)) { + PersistentExecPath = ExecPath; + } + } + + static inline auto Instance() { + return SingletonWithPriority<TExecPathsHolder, 1>(); + } + + TString ExecPath; + TString PersistentExecPath; + }; +} + +const TString& GetExecPath() { + return TExecPathsHolder::Instance()->ExecPath; +} + +const TString& GetPersistentExecPath() { + return TExecPathsHolder::Instance()->PersistentExecPath; +} |