process.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * Copyright (c) 2012-2018 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. #include "core/memory/temp_allocator.h"
  6. #include "core/process.h"
  7. #include "core/strings/string_stream.h"
  8. #if CROWN_PLATFORM_POSIX
  9. #include <unistd.h> // fork, execvp
  10. #include <sys/wait.h> // waitpid
  11. #include <errno.h>
  12. #elif CROWN_PLATFORM_WINDOWS
  13. #include <windows.h>
  14. #endif
  15. namespace crown
  16. {
  17. struct Private
  18. {
  19. #if CROWN_PLATFORM_POSIX
  20. FILE* file;
  21. pid_t pid;
  22. #elif CROWN_PLATFORM_WINDOWS
  23. PROCESS_INFORMATION process;
  24. #endif
  25. };
  26. namespace process_internal
  27. {
  28. bool is_open(Private* priv)
  29. {
  30. #if CROWN_PLATFORM_POSIX
  31. return priv->pid != -1;
  32. #elif CROWN_PLATFORM_WINDOWS
  33. return priv->process.hProcess != 0;
  34. #endif
  35. }
  36. }
  37. Process::Process()
  38. {
  39. CE_STATIC_ASSERT(sizeof(_data) >= sizeof(Private));
  40. Private* priv = (Private*)_data;
  41. #if CROWN_PLATFORM_POSIX
  42. priv->pid = -1;
  43. #elif CROWN_PLATFORM_WINDOWS
  44. memset(&priv->process, 0, sizeof(priv->process));
  45. #endif
  46. }
  47. Process::~Process()
  48. {
  49. Private* priv = (Private*)_data;
  50. CE_ENSURE(process_internal::is_open(priv) == false);
  51. }
  52. s32 Process::spawn(const char* const* argv, u32 flags)
  53. {
  54. Private* priv = (Private*)_data;
  55. CE_ENSURE(process_internal::is_open(priv) == false);
  56. #if CROWN_PLATFORM_POSIX
  57. // https://opensource.apple.com/source/Libc/Libc-167/gen.subproj/popen.c.auto.html
  58. int fildes[2];
  59. pid_t pid;
  60. if (flags & ProcessFlags::STDIN_PIPE || flags & ProcessFlags::STDOUT_PIPE)
  61. {
  62. if (pipe(fildes) < 0)
  63. return -1;
  64. }
  65. pid = fork();
  66. if (pid == -1) // Error, cleanup and return
  67. {
  68. close(fildes[0]);
  69. close(fildes[1]);
  70. return -1;
  71. }
  72. else if (pid == 0) // Child
  73. {
  74. if (flags & ProcessFlags::STDOUT_PIPE)
  75. {
  76. if (fildes[1] != STDOUT_FILENO)
  77. {
  78. dup2(fildes[1], STDOUT_FILENO);
  79. close(fildes[1]);
  80. fildes[1] = STDOUT_FILENO;
  81. }
  82. close(fildes[0]);
  83. if (flags & ProcessFlags::STDERR_MERGE)
  84. {
  85. dup2(fildes[1], 2);
  86. }
  87. }
  88. else if (flags & ProcessFlags::STDIN_PIPE)
  89. {
  90. if (fildes[0] != STDIN_FILENO)
  91. {
  92. dup2(fildes[0], STDIN_FILENO);
  93. close(fildes[0]);
  94. fildes[0] = STDIN_FILENO;
  95. }
  96. close(fildes[1]);
  97. }
  98. execvp(argv[0], (char* const*)argv);
  99. // exec returned error
  100. return -1;
  101. }
  102. // Parent
  103. if (flags & ProcessFlags::STDOUT_PIPE)
  104. {
  105. priv->file = fdopen(fildes[0], "r");
  106. close(fildes[1]);
  107. }
  108. else if (flags & ProcessFlags::STDIN_PIPE)
  109. {
  110. priv->file = fdopen(fildes[1], "w");
  111. close(fildes[0]);
  112. }
  113. else
  114. {
  115. priv->file = NULL;
  116. }
  117. priv->pid = pid;
  118. return 0;
  119. #elif CROWN_PLATFORM_WINDOWS
  120. TempAllocator512 ta;
  121. StringStream path(ta);
  122. for (s32 i = 0; argv[i] != NULL; ++i)
  123. {
  124. const char* arg = argv[i];
  125. for (; *arg; ++arg)
  126. {
  127. if (*arg == ' ')
  128. path << '\\';
  129. path << *arg;
  130. }
  131. path << ' ';
  132. }
  133. STARTUPINFO info;
  134. memset(&info, 0, sizeof(info));
  135. info.cb = sizeof(info);
  136. int err = CreateProcess(argv[0]
  137. , (LPSTR)string_stream::c_str(path)
  138. , NULL
  139. , NULL
  140. , FALSE
  141. , CREATE_NO_WINDOW
  142. , NULL
  143. , NULL
  144. , &info
  145. , &priv->process
  146. );
  147. return (s32)(err != 0 ? 0 : -err);
  148. #endif
  149. }
  150. bool Process::spawned()
  151. {
  152. Private* priv = (Private*)_data;
  153. #if CROWN_PLATFORM_POSIX
  154. return priv->pid != -1;
  155. #elif CROWN_PLATFORM_WINDOWS
  156. return priv->process.hProcess != 0;
  157. #endif
  158. }
  159. void Process::force_exit()
  160. {
  161. Private* priv = (Private*)_data;
  162. CE_ENSURE(process_internal::is_open(priv) == true);
  163. #if CROWN_PLATFORM_POSIX
  164. kill(priv->pid, SIGKILL);
  165. #elif CROWN_PLATFORM_WINDOWS
  166. #endif
  167. }
  168. s32 Process::wait()
  169. {
  170. Private* priv = (Private*)_data;
  171. CE_ENSURE(process_internal::is_open(priv) == true);
  172. #if CROWN_PLATFORM_POSIX
  173. pid_t pid;
  174. int wstatus;
  175. if (priv->file != NULL)
  176. {
  177. fclose(priv->file);
  178. priv->file = NULL;
  179. }
  180. do
  181. {
  182. pid = waitpid(priv->pid, &wstatus, 0);
  183. }
  184. while (pid == -1 && errno == EINTR);
  185. priv->pid = -1;
  186. return WIFEXITED(wstatus) ? (s32)WEXITSTATUS(wstatus) : -1;
  187. #elif CROWN_PLATFORM_WINDOWS
  188. DWORD exitcode = 1;
  189. ::WaitForSingleObject(priv->process.hProcess, INFINITE);
  190. GetExitCodeProcess(priv->process.hProcess, &exitcode);
  191. CloseHandle(priv->process.hProcess);
  192. CloseHandle(priv->process.hThread);
  193. memset(&priv->process, 0, sizeof(priv->process));
  194. return (s32)exitcode;
  195. #endif
  196. }
  197. char* Process::fgets(char* data, u32 len)
  198. {
  199. Private* priv = (Private*)_data;
  200. CE_ENSURE(process_internal::is_open(priv) == true);
  201. #if CROWN_PLATFORM_POSIX
  202. CE_ENSURE(priv->file != NULL);
  203. char* ret = ::fgets(data, len, priv->file);
  204. return ret;
  205. #elif CROWN_PLATFORM_WINDOWS
  206. return NULL;
  207. #endif
  208. }
  209. } // namespace crown