cwd.ipp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright (c) 2022 Klemens D. Morgenstern
  2. // Copyright (c) 2022 Samuel Venable
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_PROCESS_V2_IMPL_CWD_IPP
  7. #define BOOST_PROCESS_V2_IMPL_CWD_IPP
  8. #include <boost/process/v2/detail/config.hpp>
  9. #include <boost/process/v2/detail/last_error.hpp>
  10. #include <boost/process/v2/detail/throw_error.hpp>
  11. #include <boost/process/v2/ext/detail/proc_info.hpp>
  12. #include <boost/process/v2/ext/cwd.hpp>
  13. #include <string>
  14. #if defined(BOOST_PROCESS_V2_WINDOWS)
  15. #include <windows.h>
  16. #else
  17. #include <climits>
  18. #endif
  19. #if (defined(__APPLE__) && defined(__MACH__))
  20. #include <sys/proc_info.h>
  21. #include <libproc.h>
  22. #endif
  23. #if (defined(BOOST_PROCESS_V2_WINDOWS) || defined(__linux__) || defined(__ANDROID__) || defined(__sun))
  24. #include <cstdlib>
  25. #endif
  26. #if defined(__FreeBSD__)
  27. #include <sys/socket.h>
  28. #include <sys/sysctl.h>
  29. #include <sys/param.h>
  30. #include <sys/queue.h>
  31. #include <sys/user.h>
  32. #include <libprocstat.h>
  33. #endif
  34. #if (defined(__NetBSD__) || defined(__OpenBSD__))
  35. #include <sys/types.h>
  36. #include <sys/sysctl.h>
  37. #endif
  38. #if defined(__DragonFly__)
  39. #include <cstring>
  40. #include <cstdio>
  41. #endif
  42. #ifdef BOOST_PROCESS_USE_STD_FS
  43. namespace filesystem = std::filesystem;
  44. #else
  45. namespace filesystem = boost::filesystem;
  46. #endif
  47. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  48. namespace ext {
  49. #if defined(BOOST_PROCESS_V2_WINDOWS)
  50. filesystem::path cwd(HANDLE proc, boost::system::error_code & ec)
  51. {
  52. auto buffer = boost::process::v2::detail::ext::cwd_cmd_from_proc(proc, 1/*=MEMCWD*/, ec);
  53. if (!buffer.empty())
  54. return filesystem::canonical(buffer, ec);
  55. else
  56. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  57. return "";
  58. }
  59. filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  60. {
  61. struct del
  62. {
  63. void operator()(HANDLE h)
  64. {
  65. ::CloseHandle(h);
  66. };
  67. };
  68. std::unique_ptr<void, del> proc{detail::ext::open_process_with_debug_privilege(pid, ec)};
  69. if (proc == nullptr)
  70. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  71. else
  72. return cwd(proc.get(), ec);
  73. return {};
  74. }
  75. filesystem::path cwd(HANDLE proc)
  76. {
  77. boost::system::error_code ec;
  78. auto res = cwd(proc, ec);
  79. if (ec)
  80. detail::throw_error(ec, "cwd");
  81. return res;
  82. }
  83. #elif (defined(__APPLE__) && defined(__MACH__))
  84. filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  85. {
  86. proc_vnodepathinfo vpi;
  87. if (proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi)) > 0)
  88. return filesystem::canonical(vpi.pvi_cdir.vip_path, ec);
  89. else
  90. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  91. return "";
  92. }
  93. #elif (defined(__linux__) || defined(__ANDROID__) || defined(__sun))
  94. filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  95. {
  96. #if (defined(__linux__) || defined(__ANDROID__))
  97. return filesystem::canonical(
  98. filesystem::path("/proc") / std::to_string(pid) / "cwd", ec
  99. );
  100. #elif defined(__sun)
  101. return fileystem::canonical(
  102. filesystem::path("/proc") / std::to_string(pid) / "path/cwd", ec
  103. );
  104. #endif
  105. }
  106. #elif defined(__FreeBSD__)
  107. // FIXME: Add error handling.
  108. filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  109. {
  110. filesystem::path path;
  111. unsigned cntp = 0;
  112. procstat *proc_stat = procstat_open_sysctl();
  113. if (proc_stat) {
  114. kinfo_proc *proc_info = procstat_getprocs(proc_stat, KERN_PROC_PID, pid, &cntp);
  115. if (proc_info) {
  116. filestat_list *head = procstat_getfiles(proc_stat, proc_info, 0);
  117. if (head) {
  118. filestat *fst = nullptr;
  119. STAILQ_FOREACH(fst, head, next) {
  120. if (fst->fs_uflags & PS_FST_UFLAG_CDIR)
  121. {
  122. path = filesystem::canonical(fst->fs_path, ec);
  123. }
  124. }
  125. procstat_freefiles(proc_stat, head);
  126. }
  127. else
  128. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  129. procstat_freeprocs(proc_stat, proc_info);
  130. }
  131. else
  132. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  133. procstat_close(proc_stat);
  134. }
  135. else
  136. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  137. return path;
  138. }
  139. #elif defined(__DragonFly__)
  140. // FIXME: Add error handling.
  141. filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  142. {
  143. filesystem::path path;
  144. /* Probably the hackiest thing ever we are doing here, because the "official" API is broken OS-level. */
  145. FILE *fp = popen(("pos=`ans=\\`/usr/bin/fstat -w -p " + std::to_string(pid) + " | /usr/bin/sed -n 1p\\`; " +
  146. "/usr/bin/awk -v ans=\"$ans\" 'BEGIN{print index(ans, \"INUM\")}'`; str=`/usr/bin/fstat -w -p " +
  147. std::to_string(pid) + " | /usr/bin/sed -n 3p`; /usr/bin/awk -v str=\"$str\" -v pos=\"$pos\" " +
  148. "'BEGIN{print substr(str, 0, pos + 4)}' | /usr/bin/awk 'NF{NF--};1 {$1=$2=$3=$4=\"\"; print" +
  149. " substr($0, 5)'}").c_str(), "r");
  150. if (fp)
  151. {
  152. char buffer[PATH_MAX];
  153. if (fgets(buffer, sizeof(buffer), fp))
  154. {
  155. std::string str = buffer;
  156. std::size_t pos = str.find("\n", strlen(buffer) - 1);
  157. if (pos != std::string::npos)
  158. {
  159. str.replace(pos, 1, "");
  160. }
  161. path = filesystem::canonical(str.c_str(), ec);
  162. }
  163. else
  164. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  165. pclose(fp);
  166. }
  167. else
  168. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  169. return path;
  170. }
  171. #elif (defined(__NetBSD__) || defined(__OpenBSD__))
  172. filesystem::path cwd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  173. {
  174. std::string path;
  175. #if defined(__NetBSD__)
  176. int mib[4] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD};
  177. const std::size_t sz = 4;
  178. #elif defined(__OpenBSD__)
  179. int mib[3] = {CTL_KERN, KERN_PROC_CWD, pid};
  180. const std::size_t sz = 3;
  181. #endif
  182. std::size_t len = 0;
  183. if (sysctl(mib, sz, nullptr, &len, nullptr, 0) == 0)
  184. {
  185. std::string strbuff;
  186. strbuff.resize(len);
  187. if (sysctl(mib, 4, &strbuff[0], &len, nullptr, 0) == 0)
  188. {
  189. filesystem::canonical(strbuff, ec);
  190. }
  191. else
  192. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  193. }
  194. return "";
  195. }
  196. #else
  197. #error "Platform not supported"
  198. #endif
  199. filesystem::path cwd(boost::process::v2::pid_type pid)
  200. {
  201. boost::system::error_code ec;
  202. auto res = cwd(pid, ec);
  203. if (ec)
  204. detail::throw_error(ec, "cwd");
  205. return res;
  206. }
  207. } // namespace ext
  208. BOOST_PROCESS_V2_END_NAMESPACE
  209. #endif // BOOST_PROCESS_V2_IMPL_CWD_IPP