cproc.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include <errno.h>
  5. #include <signal.h>
  6. #include "cproc.h"
  7. void free_cpi(struct child_process_info* cpi, char freeOB) {
  8. if(cpi->output_buffer && freeOB) free(cpi->output_buffer);
  9. if(cpi->f_stdin) fclose(cpi->f_stdin);
  10. if(cpi->f_stdout) fclose(cpi->f_stdout);
  11. if(cpi->f_stderr) fclose(cpi->f_stderr);
  12. if(cpi->child_stdin) close(cpi->child_stdin);
  13. if(cpi->child_stdout) close(cpi->child_stdout);
  14. if(cpi->child_stderr) close(cpi->child_stderr);
  15. if(cpi->pty) close(cpi->pty);
  16. free(cpi);
  17. }
  18. void read_cpi(struct child_process_info* cpi) {
  19. while(1) {
  20. if(cpi->buf_len > cpi->buf_alloc - 128) {
  21. cpi->buf_alloc *= 2;
  22. cpi->output_buffer = realloc(cpi->output_buffer, cpi->buf_alloc * sizeof(*cpi->output_buffer));
  23. }
  24. int ret = read(cpi->pty, cpi->output_buffer + cpi->buf_len, cpi->buf_alloc - cpi->buf_len - 1);
  25. if(ret > 0) cpi->buf_len += ret;
  26. else return;
  27. }
  28. return ;
  29. }
  30. int execute_mt(strlist* cmds, int threads, struct child_process_info*** cpis) {
  31. int ret = 0;
  32. int running = 0;
  33. struct child_process_info** procs = calloc(1, cmds->len * sizeof(*procs));
  34. if(cpis) *cpis = procs;
  35. int waiting = cmds->len;
  36. while(waiting > 0) {
  37. for(int i = 0; i < cmds->len; i++) {
  38. // keep the cores full
  39. if(!procs[i] && running < threads) {
  40. procs[i] = exec_cmdline_pipe(cmds->entries[i]);
  41. procs[i]->state = 'r';
  42. running++;
  43. }
  44. if(!procs[i] || procs[i]->state == 'd') continue;
  45. read_cpi(procs[i]);
  46. int status;
  47. // returns 0 if nothing happened, -1 on error, childpid if it exited
  48. int pid = waitpid(procs[i]->pid, &status, WNOHANG);
  49. if(pid != 0) {
  50. procs[i]->state = 'd';
  51. waiting--;
  52. running--;
  53. read_cpi(procs[i]);
  54. procs[i]->output_buffer[procs[i]->buf_len] = 0;
  55. procs[i]->exit_status = WEXITSTATUS(status);
  56. if(!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
  57. // printf("error on pid %d/%d = %d\n", pid, procs[i]->pid, WEXITSTATUS(status));
  58. // printf("%s\n", procs[i]->output_buffer);
  59. ret = 1;
  60. }
  61. }
  62. }
  63. usleep(100);
  64. }
  65. return ret;
  66. }
  67. static int count_args(char* str) {
  68. int n = 0;
  69. char inspace = 1;
  70. char inquote = 0;
  71. char last_was_slash = 0;
  72. for(char* s = str; *s; s++) {
  73. if(*s == ' ') {
  74. if(!last_was_slash && !inquote) {
  75. if(!inspace) {
  76. inspace = 1;
  77. }
  78. }
  79. last_was_slash = 0;
  80. }
  81. else {
  82. if(inspace) {
  83. n++;
  84. inspace = 0;
  85. }
  86. if(*s == '\\') {
  87. if(!last_was_slash) last_was_slash = 1;
  88. }
  89. else {
  90. if(*s == '"') {
  91. if(!last_was_slash) {
  92. inquote = !inquote;
  93. }
  94. }
  95. last_was_slash = 0;
  96. }
  97. }
  98. }
  99. return n;
  100. }
  101. static char** split_args(char* cmdline, int* nargs_out) {
  102. int nargs = count_args(cmdline);
  103. char** args = malloc((nargs + 1) * sizeof(char*));
  104. int n = 0;
  105. char inspace = 1;
  106. char inquote = 0;
  107. char last_was_slash = 0;
  108. char* start = NULL;
  109. char* s;
  110. char* end = NULL;
  111. for(s = cmdline; *s; s++) {
  112. if(*s == '\'') continue;
  113. if(*s == ' ') {
  114. if(!last_was_slash && !inquote) {
  115. if(!inspace) {
  116. inspace = 1;
  117. end = s;
  118. }
  119. }
  120. last_was_slash = 0;
  121. }
  122. else {
  123. if(inspace) {
  124. if(start) {
  125. args[n++] = strndup(start, end - start);
  126. }
  127. start = s;
  128. inspace = 0;
  129. }
  130. if(*s == '\\') {
  131. if(!last_was_slash) last_was_slash = 1;
  132. }
  133. else {
  134. if(*s == '"') {
  135. if(!last_was_slash) {
  136. inquote = !inquote;
  137. }
  138. }
  139. last_was_slash = 0;
  140. }
  141. end = s;
  142. }
  143. }
  144. if(start != s && end != start) {
  145. args[n++] = strndup(start, end - start + (*end != ' '));
  146. }
  147. args[nargs] = NULL;
  148. if(nargs_out) *nargs_out = nargs;
  149. return args;
  150. }
  151. struct child_process_info* exec_cmdline_pipe(char* cmdline) {
  152. // char* test[] = {
  153. // " a ",
  154. // " a b ",
  155. // " aaa bb ",
  156. // " aaa bb",
  157. // " a b c ",
  158. // " a\\ \\ b c ",
  159. // "\\ a b c ",
  160. // "\" a b\" c ",
  161. // "\" a\\ b\" c ",
  162. // "\" a\\ b\"\\ c ",
  163. // "\" a\\ b\"\\ c",
  164. // NULL,
  165. // };
  166. //
  167. // for(char** x = test; *x; x++) {
  168. // printf("[%s] = %d\n", *x, count_args(*x));
  169. // char** a = split_args(*x, NULL);
  170. // for(int i = 0; a[i]; i++) {
  171. // printf(" %d = [%s]\n", i, a[i]);
  172. // }
  173. // }
  174. //
  175. char** args = split_args(cmdline, NULL);
  176. struct child_process_info* cpi = exec_process_pipe(args[0], args);
  177. // printf("executing '%s'\n", cmdline);
  178. // for(char** s = args; *s; s++) printf("%ld - '%s'\n", (s-args), *s);
  179. free_strpp(args);
  180. return cpi;
  181. }
  182. struct child_process_info* exec_process_pipe(char* exec_path, char* args[]) {
  183. int in[2]; // io pipes
  184. int out[2];
  185. int err[2];
  186. const int RE = 0;
  187. const int WR = 1;
  188. // 0 = read, 1 = write
  189. if(pipe(in) < 0) {
  190. return NULL;
  191. }
  192. if(pipe(out) < 0) {
  193. close(in[0]);
  194. close(in[1]);
  195. return NULL;
  196. }
  197. if(pipe(err) < 0) {
  198. close(in[0]);
  199. close(in[1]);
  200. close(out[0]);
  201. close(out[1]);
  202. return NULL;
  203. }
  204. errno = 0;
  205. int childPID = fork();
  206. if(childPID == -1) {
  207. fprintf(stderr, "failed to fork trying to execute '%s'\n", exec_path);
  208. perror(strerror(errno));
  209. return NULL;
  210. }
  211. else if(childPID == 0) { // child process
  212. // redirect standard fd's to the pipe fd's
  213. if(dup2(in[RE], fileno(stdin)) == -1) {
  214. printf("failed 1\n");
  215. exit(errno);
  216. }
  217. if(dup2(out[WR], fileno(stdout)) == -1) {
  218. printf("failed 2\n");
  219. exit(errno);
  220. }
  221. if(dup2(err[WR], fileno(stderr)) == -1) {
  222. printf("failed 3\n");
  223. exit(errno);
  224. }
  225. // close original fd's used by the parent
  226. close(in[0]);
  227. close(in[1]);
  228. close(out[0]);
  229. close(out[1]);
  230. close(err[0]);
  231. close(err[1]);
  232. // fcntl(in[WR], F_SETFL, fcntl(in[WR], F_GETFL) | O_NONBLOCK);
  233. // die when the parent does (linux only)
  234. prctl(PR_SET_PDEATHSIG, SIGHUP);
  235. // swap for the desired program
  236. execvp(exec_path, args); // never returns if successful
  237. fprintf(stderr, "failed to execute '%s'\n", exec_path);
  238. exit(1); // kill the forked process
  239. }
  240. else { // parent process
  241. // close the child-end of the pipes
  242. struct child_process_info* cpi;
  243. cpi = calloc(1, sizeof(*cpi));
  244. cpi->child_stdin = in[WR];
  245. cpi->child_stdout = out[RE];
  246. cpi->child_stderr = err[RE];
  247. //cpi->f_stdin = fdopen(cpi->child_stdin, "wb"); // disabled for git clone debugging
  248. //cpi->f_stdout = fdopen(cpi->child_stdout, "rb");
  249. //cpi->f_stderr = fdopen(cpi->child_stderr, "rb");
  250. // set to non-blocking
  251. fcntl(cpi->child_stdout, F_SETFL, fcntl(cpi->child_stdout, F_GETFL) | O_NONBLOCK);
  252. fcntl(cpi->child_stderr, F_SETFL, fcntl(cpi->child_stderr, F_GETFL) | O_NONBLOCK);
  253. close(in[0]);
  254. close(out[1]);
  255. close(err[1]);
  256. cpi->pid = childPID;
  257. // int status;
  258. // returns 0 if nothing happened, -1 on error
  259. // pid = waitpid(childPID, &status, WNOHANG);
  260. return cpi;
  261. }
  262. return NULL; // shouldn't reach here
  263. }