run.hpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. #pragma once
  2. //auto execute(const string& name, const string& args...) -> string;
  3. //[[synchronous]]
  4. //executes program, waits for completion, and returns data written to stdout
  5. //auto invoke(const string& name, const string& args...) -> void;
  6. //[[asynchronous]]
  7. //if a program is specified, it is executed with the arguments provided
  8. //if a file is specified, the file is opened using the program associated with said file type
  9. //if a folder is specified, the folder is opened using the associated file explorer
  10. //if a URL is specified, the default web browser is opened and pointed at the URL requested
  11. #include <nall/intrinsics.hpp>
  12. #include <nall/string.hpp>
  13. namespace nall {
  14. struct execute_result_t {
  15. explicit operator bool() const { return code == EXIT_SUCCESS; }
  16. int code = EXIT_FAILURE;
  17. string output;
  18. string error;
  19. };
  20. #if defined(PLATFORM_MACOS) || defined(PLATFORM_LINUX) || defined(PLATFORM_BSD)
  21. template<typename... P> inline auto execute(const string& name, P&&... p) -> execute_result_t {
  22. int fdout[2];
  23. int fderr[2];
  24. if(pipe(fdout) == -1) return {};
  25. if(pipe(fderr) == -1) return {};
  26. pid_t pid = fork();
  27. if(pid == 0) {
  28. const char* argv[1 + sizeof...(p) + 1];
  29. const char** argp = argv;
  30. vector<string> argl(forward<P>(p)...);
  31. *argp++ = (const char*)name;
  32. for(auto& arg : argl) *argp++ = (const char*)arg;
  33. *argp++ = nullptr;
  34. dup2(fdout[1], STDOUT_FILENO);
  35. dup2(fderr[1], STDERR_FILENO);
  36. close(fdout[0]);
  37. close(fderr[0]);
  38. close(fdout[1]);
  39. close(fderr[1]);
  40. execvp(name, (char* const*)argv);
  41. //this is called only if execvp fails:
  42. //use _exit instead of exit, to avoid destroying key shared file descriptors
  43. _exit(EXIT_FAILURE);
  44. } else {
  45. close(fdout[1]);
  46. close(fderr[1]);
  47. char buffer[256];
  48. execute_result_t result;
  49. while(true) {
  50. auto size = read(fdout[0], buffer, sizeof(buffer));
  51. if(size <= 0) break;
  52. auto offset = result.output.size();
  53. result.output.resize(offset + size);
  54. memory::copy(result.output.get() + offset, buffer, size);
  55. }
  56. while(true) {
  57. auto size = read(fderr[0], buffer, sizeof(buffer));
  58. if(size <= 0) break;
  59. auto offset = result.error.size();
  60. result.error.resize(offset + size);
  61. memory::copy(result.error.get() + offset, buffer, size);
  62. }
  63. close(fdout[0]);
  64. close(fderr[0]);
  65. int status = 0;
  66. waitpid(pid, &status, 0);
  67. if(!WIFEXITED(status)) return {};
  68. result.code = WEXITSTATUS(status);
  69. return result;
  70. }
  71. }
  72. template<typename... P> inline auto invoke(const string& name, P&&... p) -> void {
  73. pid_t pid = fork();
  74. if(pid == 0) {
  75. const char* argv[1 + sizeof...(p) + 1];
  76. const char** argp = argv;
  77. vector<string> argl(forward<P>(p)...);
  78. *argp++ = (const char*)name;
  79. for(auto& arg : argl) *argp++ = (const char*)arg;
  80. *argp++ = nullptr;
  81. if(execvp(name, (char* const*)argv) < 0) {
  82. #if defined(PLATFORM_MACOS)
  83. execlp("open", "open", (const char*)name, nullptr);
  84. #else
  85. execlp("xdg-open", "xdg-open", (const char*)name, nullptr);
  86. #endif
  87. }
  88. exit(0);
  89. }
  90. }
  91. #elif defined(PLATFORM_WINDOWS)
  92. template<typename... P> inline auto execute(const string& name, P&&... p) -> execute_result_t {
  93. vector<string> argl(name, forward<P>(p)...);
  94. for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""};
  95. string arguments = argl.merge(" ");
  96. SECURITY_ATTRIBUTES sa;
  97. ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
  98. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  99. sa.bInheritHandle = true;
  100. sa.lpSecurityDescriptor = nullptr;
  101. HANDLE stdoutRead;
  102. HANDLE stdoutWrite;
  103. if(!CreatePipe(&stdoutRead, &stdoutWrite, &sa, 0)) return {};
  104. if(!SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0)) return {};
  105. HANDLE stderrRead;
  106. HANDLE stderrWrite;
  107. if(!CreatePipe(&stderrRead, &stderrWrite, &sa, 0)) return {};
  108. if(!SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0)) return {};
  109. HANDLE stdinRead;
  110. HANDLE stdinWrite;
  111. if(!CreatePipe(&stdinRead, &stdinWrite, &sa, 0)) return {};
  112. if(!SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0)) return {};
  113. STARTUPINFO si;
  114. ZeroMemory(&si, sizeof(STARTUPINFO));
  115. si.cb = sizeof(STARTUPINFO);
  116. si.hStdOutput = stdoutWrite;
  117. si.hStdError = stderrWrite;
  118. si.hStdInput = stdinRead;
  119. si.dwFlags = STARTF_USESTDHANDLES;
  120. PROCESS_INFORMATION pi;
  121. ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
  122. if(!CreateProcess(
  123. nullptr, utf16_t(arguments),
  124. nullptr, nullptr, true, CREATE_NO_WINDOW,
  125. nullptr, nullptr, &si, &pi
  126. )) return {};
  127. DWORD exitCode = EXIT_FAILURE;
  128. if(WaitForSingleObject(pi.hProcess, INFINITE)) return {};
  129. if(!GetExitCodeProcess(pi.hProcess, &exitCode)) return {};
  130. CloseHandle(pi.hThread);
  131. CloseHandle(pi.hProcess);
  132. char buffer[256];
  133. execute_result_t result;
  134. result.code = exitCode;
  135. while(true) {
  136. DWORD read, available, remaining;
  137. if(!PeekNamedPipe(stdoutRead, nullptr, sizeof(buffer), &read, &available, &remaining)) break;
  138. if(read == 0) break;
  139. if(!ReadFile(stdoutRead, buffer, sizeof(buffer), &read, nullptr)) break;
  140. if(read == 0) break;
  141. auto offset = result.output.size();
  142. result.output.resize(offset + read);
  143. memory::copy(result.output.get() + offset, buffer, read);
  144. }
  145. while(true) {
  146. DWORD read, available, remaining;
  147. if(!PeekNamedPipe(stderrRead, nullptr, sizeof(buffer), &read, &available, &remaining)) break;
  148. if(read == 0) break;
  149. if(!ReadFile(stderrRead, buffer, sizeof(buffer), &read, nullptr)) break;
  150. if(read == 0) break;
  151. auto offset = result.error.size();
  152. result.error.resize(offset + read);
  153. memory::copy(result.error.get() + offset, buffer, read);
  154. }
  155. return result;
  156. }
  157. template<typename... P> inline auto invoke(const string& name, P&&... p) -> void {
  158. vector<string> argl(forward<P>(p)...);
  159. for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""};
  160. string arguments = argl.merge(" ");
  161. string directory = Path::program().replace("/", "\\");
  162. ShellExecute(nullptr, nullptr, utf16_t(name), utf16_t(arguments), utf16_t(directory), SW_SHOWNORMAL);
  163. }
  164. #else
  165. template<typename... P> inline auto execute(const string& name, P&&... p) -> string {
  166. return "";
  167. }
  168. template<typename... P> inline auto invoke(const string& name, P&&... p) -> void {
  169. }
  170. #endif
  171. }