sq_spawnx.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. //adapted from http://mysite.mweb.co.za/residents/sdonovan/lua/lua-gdb.zip
  2. #ifdef SQ_USE_SPAWNX
  3. #include <sys/types.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <ctype.h>
  9. #include "squirrel.h"
  10. #include "sqstdblobimpl.h"
  11. #ifdef WIN32
  12. #include <windows.h>
  13. #else
  14. #ifdef __cplusplus
  15. extern "C" {
  16. #endif
  17. #ifdef __MACH__
  18. #include <util.h>
  19. int openpty(int *, int *, char *, struct termios *, struct winsize *);
  20. pid_t forkpty(int *, char *, struct termios *, struct winsize *);
  21. #else
  22. #include <pty.h>
  23. #endif
  24. #include <unistd.h>
  25. #include <termios.h>
  26. #ifdef __cplusplus
  27. }
  28. #endif
  29. #endif
  30. struct SQ_SpawnX {
  31. #ifdef WIN32
  32. HANDLE hPipeRead,hWriteSubProcess;
  33. PROCESS_INFORMATION pi;
  34. #else
  35. int spawn_fd;
  36. int pid;
  37. struct termios tm;
  38. #endif // WIN32
  39. };
  40. #ifdef WIN32
  41. static SQRESULT sq_spawnx_open(HSQUIRRELVM v, SQ_SpawnX *self, const char *cmd)
  42. {
  43. SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), 0, 0};
  44. SECURITY_DESCRIPTOR sd;
  45. STARTUPINFO si = {
  46. sizeof(STARTUPINFO), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  47. };
  48. HANDLE hRead2,hPipeWrite;
  49. BOOL running;
  50. HANDLE hProcess = GetCurrentProcess();
  51. sa.bInheritHandle = TRUE;
  52. sa.lpSecurityDescriptor = NULL;
  53. InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
  54. SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
  55. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  56. sa.lpSecurityDescriptor = &sd;
  57. // Create pipe for output redirection
  58. // read handle, write handle, security attributes, number of bytes reserved for pipe - 0 default
  59. CreatePipe(&self->hPipeRead, &hPipeWrite, &sa, 0);
  60. // Create pipe for input redirection. In this code, you do not
  61. // redirect the output of the child process, but you need a handle
  62. // to set the hStdInput field in the STARTUP_INFO struct. For safety,
  63. // you should not set the handles to an invalid handle.
  64. hRead2 = NULL;
  65. // read handle, write handle, security attributes, number of bytes reserved for pipe - 0 default
  66. CreatePipe(&hRead2, &self->hWriteSubProcess, &sa, 0);
  67. SetHandleInformation(self->hPipeRead, HANDLE_FLAG_INHERIT, 0);
  68. SetHandleInformation(self->hWriteSubProcess, HANDLE_FLAG_INHERIT, 0);
  69. si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  70. si.wShowWindow = SW_HIDE;
  71. si.hStdInput = hRead2;
  72. si.hStdOutput = hPipeWrite;
  73. si.hStdError = hPipeWrite;
  74. running = CreateProcess(
  75. NULL,
  76. (LPSTR)cmd,
  77. NULL, NULL,
  78. TRUE, CREATE_NEW_PROCESS_GROUP,
  79. NULL,
  80. NULL, // start directory
  81. &si, &self->pi);
  82. CloseHandle(self->pi.hThread);
  83. CloseHandle(hRead2);
  84. CloseHandle(hPipeWrite);
  85. if (running) return SQ_OK;
  86. return sq_throwerror(v, _SC("unable to spawn process"));
  87. }
  88. #else
  89. static char *quote_strtok(char *str, char str_delim)
  90. {
  91. // a specialized version of strtok() which treats quoted strings specially
  92. // (used for handling command-line parms)
  93. static char *tok;
  94. if(str != NULL) tok = str;
  95. while (*tok && isspace(*tok)) tok++;
  96. if (*tok == '\0') return NULL;
  97. if (*tok == str_delim) {
  98. tok++; // skip "
  99. str = tok;
  100. while (*tok && *tok != str_delim) tok++;
  101. } else {
  102. str = tok;
  103. while (*tok && ! isspace(*tok)) tok++;
  104. }
  105. if (*tok) *tok++ = '\0';
  106. return str;
  107. }
  108. static SQRESULT sq_spawnx_open(HSQUIRRELVM v, SQ_SpawnX *self, const char *cmd)
  109. {
  110. const char *args[30];
  111. int i = 0;
  112. char* argline = strdup(cmd);
  113. char* arg = quote_strtok(argline,'"');
  114. if (arg == NULL) return 0;
  115. while (arg != NULL) {
  116. args[i++] = arg;
  117. //fprintf(stderr,"%d %s\n",i,arg);
  118. arg = quote_strtok(NULL,'"');
  119. }
  120. args[i] = NULL;
  121. memset(&self->tm,0,sizeof(self->tm));
  122. cfmakeraw(&self->tm);
  123. errno = 0;
  124. self->pid = forkpty(&self->spawn_fd,NULL,&self->tm,NULL);
  125. if (self->pid == 0) { // child
  126. execvp(args[0], (char*const*)args);
  127. // if we get here, it's an error!
  128. perror("unable to spawn process");
  129. } else {
  130. return SQ_OK;
  131. }
  132. return sq_throwerror(v, _SC("unable to spawn process"));
  133. }
  134. #endif
  135. static const SQChar sq_spawnx_TAG[] = _SC("SQ_SpawnX");
  136. static SQRESULT get_spawnx_instance(HSQUIRRELVM v, SQInteger idx, SQ_SpawnX **spawnx_st)
  137. {
  138. if(sq_getinstanceup(v, idx, (SQUserPointer*)spawnx_st, (void*)sq_spawnx_TAG) != SQ_OK) return SQ_ERROR;
  139. if(!*spawnx_st) return sq_throwerror(v, _SC("%s"), _SC("spawnx already closed"));
  140. return SQ_OK;
  141. }
  142. #define GET_sq_spawnx_INSTANCE(v, idx) \
  143. SQ_SpawnX *self=NULL; \
  144. if(get_spawnx_instance(v, idx, &self) != SQ_OK) return SQ_ERROR;
  145. static SQRESULT sq_spawnx_releasehook(SQUserPointer p, SQInteger /*size*/, void */*ep*/)
  146. {
  147. SQ_SpawnX *self = ((SQ_SpawnX *)p);
  148. if(self)
  149. {
  150. sq_free(self, sizeof(SQ_SpawnX));
  151. }
  152. return 1;
  153. }
  154. static SQRESULT sq_spawnx_constructor(HSQUIRRELVM v)
  155. {
  156. SQ_FUNC_VARS_NO_TOP(v);
  157. SQ_GET_STRING(v, 2, cmd);
  158. SQ_SpawnX *self = (SQ_SpawnX*)sq_malloc(sizeof(SQ_SpawnX));
  159. _rc_ = sq_spawnx_open(v, self, cmd);
  160. sq_setinstanceup(v,1,self);
  161. sq_setreleasehook(v,1,sq_spawnx_releasehook);
  162. return 0;
  163. }
  164. static SQRESULT sq_spawnx_read(HSQUIRRELVM v)
  165. {
  166. SQ_FUNC_VARS(v);
  167. GET_sq_spawnx_INSTANCE(v, 1);
  168. #define DEFAULT_BUFFSIZE 2048
  169. SQ_OPT_INTEGER(v, 2, read_sz, DEFAULT_BUFFSIZE);
  170. //int isScratchString = (read_sz == DEFAULT_BUFFSIZE);
  171. //SQChar *buf = isScratchString ? sq_getscratchstr(v, read_sz) : sq_getscratchpad(v, read_sz);
  172. SQChar *buf = sq_getscratchpad(v, read_sz);
  173. #ifdef WIN32
  174. DWORD bytesRead;
  175. int res = ReadFile(self->hPipeRead,buf,read_sz, &bytesRead, NULL);
  176. if (res == 0) {
  177. //if(isScratchString) sq_delscratchstr(v, buf);
  178. return sq_throwerror(v, _SC("error reading GetLastError() = %d"), GetLastError());
  179. #else
  180. int bytesRead = read(self->spawn_fd, buf, read_sz);
  181. if(bytesRead < 0) {
  182. //if(isScratchString) sq_delscratchstr(v, buf);
  183. return sq_throwerror(v, _SC("error reading %d : %s"), errno, strerror(errno));
  184. #endif
  185. } else {
  186. /*if(isScratchString) sq_pushscratchstr(v);
  187. else*/ sq_pushstring(v,buf, bytesRead);
  188. }
  189. return 1;
  190. }
  191. static SQRESULT sq_spawnx_write(HSQUIRRELVM v)
  192. {
  193. SQ_FUNC_VARS_NO_TOP(v);
  194. GET_sq_spawnx_INSTANCE(v, 1);
  195. SQ_GET_STRING(v, 2, str);
  196. #ifdef WIN32
  197. DWORD bytesWrote;
  198. WriteFile(self->hWriteSubProcess,str,str_size,&bytesWrote, NULL);
  199. sq_pushinteger(v, bytesWrote);
  200. #else
  201. ssize_t n = write(self->spawn_fd,str,str_size);
  202. //fdatasync(self->spawn_fd);
  203. sq_pushinteger(v, n);
  204. #endif
  205. return 1;
  206. }
  207. static SQRESULT sq_spawnx_pid(HSQUIRRELVM v)
  208. {
  209. GET_sq_spawnx_INSTANCE(v, 1);
  210. #ifdef WIN32
  211. sq_pushinteger(v, (intptr_t)(self->hPipeRead));
  212. #else
  213. sq_pushinteger(v, self->pid);
  214. #endif
  215. return 1;
  216. }
  217. #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_spawnx_##name,nparams,tycheck}
  218. static SQRegFunction sq_spawnx_methods[] =
  219. {
  220. _DECL_FUNC(constructor,2,_SC(".s")),
  221. _DECL_FUNC(read,-1,_SC("xi")),
  222. _DECL_FUNC(write,2,_SC("xs")),
  223. _DECL_FUNC(pid,1,_SC("x")),
  224. {0,0}
  225. };
  226. #ifdef __cplusplus
  227. extern "C" {
  228. #endif
  229. SQRESULT sqext_register_sq_spawnx(HSQUIRRELVM v)
  230. {
  231. sq_pushstring(v, sq_spawnx_TAG, -1);
  232. sq_newclass(v, SQFalse);
  233. sq_settypetag(v,-1,(void*)sq_spawnx_TAG);
  234. sq_insert_reg_funcs(v, sq_spawnx_methods);
  235. sq_newslot(v,-3,SQTrue);
  236. return 1;
  237. }
  238. #ifdef __cplusplus
  239. }
  240. #endif
  241. #endif // SQ_USE_SPAWNX