cmd.ipp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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_CMD_IPP
  7. #define BOOST_PROCESS_V2_IMPL_CMD_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/cmd.hpp>
  13. #if defined(BOOST_PROCESS_V2_WINDOWS)
  14. #include <windows.h>
  15. #include <shellapi.h>
  16. #else
  17. #include <cstdlib>
  18. #endif
  19. #if (defined(__linux__) || defined(__ANDROID__))
  20. #include <cstdio>
  21. #endif
  22. #if defined(__FreeBSD__)
  23. #include <sys/socket.h>
  24. #include <sys/sysctl.h>
  25. #include <sys/param.h>
  26. #include <sys/queue.h>
  27. #include <sys/user.h>
  28. #include <libprocstat.h>
  29. #endif
  30. #if (defined(__DragonFly__) || defined(__OpenBSD__))
  31. #include <sys/types.h>
  32. #include <sys/param.h>
  33. #include <sys/sysctl.h>
  34. #include <sys/user.h>
  35. #include <kvm.h>
  36. #endif
  37. #if defined(__NetBSD__)
  38. #include <sys/types.h>
  39. #include <kvm.h>
  40. #include <sys/param.h>
  41. #include <sys/sysctl.h>
  42. #endif
  43. #if defined(__sun)
  44. #include <sys/types.h>
  45. #include <kvm.h>
  46. #include <sys/param.h>
  47. #include <sys/time.h>
  48. #include <sys/proc.h>
  49. #endif
  50. BOOST_PROCESS_V2_BEGIN_NAMESPACE
  51. struct make_cmd_shell_
  52. {
  53. #if defined(BOOST_PROCESS_V2_WINDOWS)
  54. static shell make(std::wstring data)
  55. {
  56. shell res;
  57. res.input_ = res.buffer_ = std::move(data);
  58. res.parse_();
  59. return res;
  60. }
  61. #else
  62. static shell make(std::string data,
  63. int argc, char ** argv,
  64. void(*free_func)(int, char**))
  65. {
  66. shell res;
  67. res.argc_ = argc;
  68. res.input_ = res.buffer_ = std::move(data);
  69. res.argv_ = argv;
  70. res.free_argv_ = free_func;
  71. return res;
  72. }
  73. static shell clone(char ** cmd)
  74. {
  75. shell res;
  76. res.argc_ = 0;
  77. std::size_t str_lengths = 0;
  78. for (auto c = cmd; *c != nullptr; c++)
  79. {
  80. res.argc_++;
  81. str_lengths += (std::strlen(*c) + 1);
  82. }
  83. // yes, not the greatest solution.
  84. res.buffer_.resize(str_lengths);
  85. res.argv_ = new char*[res.argc_ + 1];
  86. res.free_argv_ = +[](int argc, char ** argv) {delete[] argv;};
  87. res.argv_[res.argc_] = nullptr;
  88. auto p = &*res.buffer_.begin();
  89. for (int i = 0; i < res.argc_; i++)
  90. {
  91. const auto ln = std::strlen(cmd[i]);
  92. res.argv_[i] = std::strcpy(p, cmd[i]);
  93. p += (ln + 1);
  94. }
  95. return res;
  96. }
  97. #endif
  98. };
  99. namespace ext {
  100. #if defined(BOOST_PROCESS_V2_WINDOWS)
  101. shell cmd(HANDLE proc, error_code & ec)
  102. {
  103. std::wstring buffer = boost::process::v2::detail::ext::cwd_cmd_from_proc(proc, 0/*=MEMCMD*/, ec);
  104. if (!ec)
  105. return make_cmd_shell_::make(std::move(buffer));
  106. else
  107. return {};
  108. }
  109. shell cmd(HANDLE proc)
  110. {
  111. boost::system::error_code ec;
  112. auto res = cmd(proc, ec);
  113. if (ec)
  114. detail::throw_error(ec, "cmd");
  115. return res;
  116. }
  117. shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  118. {
  119. struct del
  120. {
  121. void operator()(HANDLE h)
  122. {
  123. ::CloseHandle(h);
  124. };
  125. };
  126. std::unique_ptr<void, del> proc{detail::ext::open_process_with_debug_privilege(pid, ec)};
  127. if (proc == nullptr)
  128. {
  129. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
  130. return shell{};
  131. }
  132. else
  133. return cmd(proc.get(), ec);
  134. }
  135. #elif (defined(__APPLE__) && defined(__MACH__))
  136. shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  137. {
  138. int mib[3] = {CTL_KERN, KERN_ARGMAX, 0};
  139. int argmax = 0;
  140. auto size = sizeof(argmax);
  141. if (sysctl(mib, 2, &argmax, &size, nullptr, 0) == -1)
  142. {
  143. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  144. return {};
  145. }
  146. std::string procargs;
  147. procargs.resize(argmax - 1);
  148. mib[1] = KERN_PROCARGS;
  149. mib[2] = pid;
  150. size = argmax;
  151. if (sysctl(mib, 3, &*procargs.begin(), &size, nullptr, 0) != 0)
  152. {
  153. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  154. return {};
  155. }
  156. int argc = *reinterpret_cast<const int*>(procargs.data());
  157. auto itr = procargs.begin() + sizeof(argc);
  158. std::unique_ptr<char*[]> argv{new char*[argc + 1]};
  159. const auto end = procargs.end();
  160. argv[argc] = nullptr; //args is a null-terminated list
  161. for (auto n = 0u; n <= argc; n++)
  162. {
  163. auto e = std::find(itr, end, '\0');
  164. if (e == end && n < argc) // something off
  165. {
  166. BOOST_PROCESS_V2_ASSIGN_EC(ec, EINVAL, system_category())
  167. return {};
  168. }
  169. argv[n] = &*itr;
  170. itr = e + 1; // start searching start
  171. }
  172. auto fr_func = +[](int argc, char ** argv) {delete [] argv;};
  173. return make_cmd_shell_::make(std::move(procargs), argc, argv.release(), fr_func);
  174. }
  175. #elif (defined(__linux__) || defined(__ANDROID__))
  176. shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  177. {
  178. std::string procargs;
  179. procargs.resize(4096);
  180. int f = ::open(("/proc/" + std::to_string(pid) + "/cmdline").c_str(), O_RDONLY);
  181. while (procargs.back() != EOF)
  182. {
  183. auto r = ::read(f, &*(procargs.end() - 4096), 4096);
  184. if (r < 0)
  185. {
  186. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  187. ::close(f);
  188. return {};
  189. }
  190. if (r < 4096) // done!
  191. {
  192. procargs.resize(procargs.size() - 4096 + r);
  193. break;
  194. }
  195. procargs.resize(procargs.size() + 4096);
  196. }
  197. ::close(f);
  198. if (procargs.back() == EOF)
  199. procargs.pop_back();
  200. auto argc = std::count(procargs.begin(), procargs.end(), '\0');
  201. auto itr = procargs.begin();
  202. std::unique_ptr<char*[]> argv{new char*[argc + 1]};
  203. const auto end = procargs.end();
  204. argv[argc] = nullptr; //args is a null-terminated list
  205. for (auto n = 0u; n <= argc; n++)
  206. {
  207. auto e = std::find(itr, end, '\0');
  208. if (e == end && n < argc) // something off
  209. {
  210. BOOST_PROCESS_V2_ASSIGN_EC(ec, EINVAL, system_category())
  211. return {};
  212. }
  213. argv[n] = &*itr;
  214. itr = e + 1; // start searching start
  215. }
  216. auto fr_func = +[](int argc, char ** argv) {delete [] argv;};
  217. return make_cmd_shell_::make(std::move(procargs), argc, argv.release(), fr_func);
  218. }
  219. #elif defined(__FreeBSD__)
  220. shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  221. {
  222. struct cl_proc_stat
  223. {
  224. void operator()(struct procstat *proc_stat)
  225. {
  226. procstat_close(proc_stat);
  227. }
  228. };
  229. std::unique_ptr<struct procstat, cl_proc_stat> proc_stat{procstat_open_sysctl()};
  230. if (!proc_stat)
  231. {
  232. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  233. return {};
  234. }
  235. struct proc_info_close
  236. {
  237. struct procstat * proc_stat;
  238. void operator()(struct kinfo_proc * proc_info)
  239. {
  240. procstat_freeprocs(proc_stat, proc_info);
  241. }
  242. };
  243. unsigned cntp;
  244. std::unique_ptr<struct kinfo_proc, proc_info_close> proc_info{
  245. procstat_getprocs(proc_stat.get(), KERN_PROC_PID, pid, &cntp),
  246. proc_info_close{proc_stat.get()}};
  247. if (!proc_info)
  248. {
  249. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  250. return {};
  251. }
  252. char **cmd = procstat_getargv(proc_stat.get(), proc_info.get(), 0);
  253. if (!cmd)
  254. {
  255. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  256. return {};
  257. }
  258. auto res = make_cmd_shell_::clone(cmd);
  259. procstat_freeargv(proc_stat.get());
  260. return res;
  261. }
  262. #elif defined(__DragonFly__)
  263. shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  264. {
  265. int cntp = 0;
  266. kinfo_proc *proc_info = nullptr;
  267. const char *nlistf, *memf;
  268. nlistf = memf = "/dev/null";
  269. struct closer
  270. {
  271. void operator()(kvm_t * kd)
  272. {
  273. kvm_close(kd);
  274. }
  275. };
  276. std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nlistf, memf, nullptr, O_RDONLY, nullptr)};
  277. if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return {};}
  278. if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, &cntp)))
  279. {
  280. char **cmd = kvm_getargv(kd.get(), proc_info, 0);
  281. if (cmd)
  282. return make_cmd_shell_::clone(cmd);
  283. else
  284. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  285. }
  286. else
  287. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  288. return {};
  289. }
  290. #elif defined(__NetBSD__)
  291. shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  292. {
  293. std::vector<std::string> vec;
  294. int cntp = 0;
  295. kinfo_proc2 *proc_info = nullptr;
  296. struct closer
  297. {
  298. void operator()(kvm_t * kd)
  299. {
  300. kvm_close(kd);
  301. }
  302. };
  303. std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)};
  304. if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return vec;}
  305. if ((proc_info = kvm_getproc2(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc2), &cntp)))
  306. {
  307. char **cmd = kvm_getargv2(kd.get(), proc_info, 0);
  308. if (cmd)
  309. return make_cmd_shell_::clone(cmd);
  310. else
  311. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  312. }
  313. else
  314. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  315. return vec;
  316. }
  317. #elif defined(__OpenBSD__)
  318. shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  319. {
  320. std::vector<std::string> vec;
  321. int cntp = 0;
  322. kinfo_proc *proc_info = nullptr;
  323. struct closer
  324. {
  325. void operator()(kvm_t * kd)
  326. {
  327. kvm_close(kd);
  328. }
  329. };
  330. std::unique_ptr<kvm_t, closer> kd{kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr)};
  331. if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return vec;}
  332. if ((proc_info = kvm_getprocs(kd.get(), KERN_PROC_PID, pid, sizeof(struct kinfo_proc), &cntp)))
  333. {
  334. char **cmd = kvm_getargv(kd.get(), proc_info, 0);
  335. if (cmd)
  336. return make_cmd_shell_::clone(cmd);
  337. else
  338. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  339. }
  340. else
  341. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  342. kvm_close(kd);
  343. return {};
  344. }
  345. #elif defined(__sun)
  346. shell cmd(boost::process::v2::pid_type pid, boost::system::error_code & ec)
  347. {
  348. char **cmd = nullptr;
  349. proc *proc_info = nullptr;
  350. user *proc_user = nullptr;
  351. kd = kvm_open(nullptr, nullptr, nullptr, O_RDONLY, nullptr);
  352. if (!kd) {BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) return {};}
  353. if ((proc_info = kvm_getproc(kd, pid)))
  354. {
  355. if ((proc_user = kvm_getu(kd, proc_info)))
  356. {
  357. if (!kvm_getcmd(kd, proc_info, proc_user, &cmd, nullptr))
  358. {
  359. int argc = 0;
  360. for (int i = 0; cmd[i] != nullptr; i++)
  361. argc ++;
  362. return make_cmd_shell_::make(
  363. {}, argc, cmd,
  364. +[](int, char ** argv) {::free(argv);})
  365. }
  366. else
  367. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  368. }
  369. else
  370. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  371. }
  372. else
  373. BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
  374. kvm_close(kd);
  375. return {};
  376. }
  377. #else
  378. #error "Platform not supported"
  379. #endif
  380. shell cmd(boost::process::v2::pid_type pid)
  381. {
  382. boost::system::error_code ec;
  383. auto res = cmd(pid, ec);
  384. if (ec)
  385. detail::throw_error(ec, "cmd");
  386. return res;
  387. }
  388. } // namespace ext
  389. BOOST_PROCESS_V2_END_NAMESPACE
  390. #endif // BOOST_PROCESS_V2_IMPL_CMD_IPP