simple_exec.h 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // copied from: https://github.com/wheybags/simple_exec/blob/5a74c507c4ce1b2bb166177ead4cca7cfa23cb35/simple_exec.h
  2. // simple_exec.h, single header library to run external programs + retrieve their status code and output (unix only for now)
  3. //
  4. // do this:
  5. // #define SIMPLE_EXEC_IMPLEMENTATION
  6. // before you include this file in *one* C or C++ file to create the implementation.
  7. // i.e. it should look like this:
  8. // #define SIMPLE_EXEC_IMPLEMENTATION
  9. // #include "simple_exec.h"
  10. #ifndef SIMPLE_EXEC_H
  11. #define SIMPLE_EXEC_H
  12. int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...);
  13. int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs);
  14. #endif // SIMPLE_EXEC_H
  15. #ifdef SIMPLE_EXEC_IMPLEMENTATION
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <unistd.h>
  20. #include <assert.h>
  21. #include <sys/wait.h>
  22. #include <stdarg.h>
  23. #include <fcntl.h>
  24. #define release_assert(exp) { if (!(exp)) { abort(); } }
  25. enum PIPE_FILE_DESCRIPTORS
  26. {
  27. READ_FD = 0,
  28. WRITE_FD = 1
  29. };
  30. enum RUN_COMMAND_ERROR
  31. {
  32. COMMAND_RAN_OK = 0,
  33. COMMAND_NOT_FOUND = 1
  34. };
  35. int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs)
  36. {
  37. // adapted from: https://stackoverflow.com/a/479103
  38. int bufferSize = 256;
  39. char buffer[bufferSize + 1];
  40. int dataReadFromChildDefaultSize = bufferSize * 5;
  41. int dataReadFromChildSize = dataReadFromChildDefaultSize;
  42. int dataReadFromChildUsed = 0;
  43. char* dataReadFromChild = (char*)malloc(dataReadFromChildSize);
  44. int parentToChild[2];
  45. release_assert(pipe(parentToChild) == 0);
  46. int childToParent[2];
  47. release_assert(pipe(childToParent) == 0);
  48. int errPipe[2];
  49. release_assert(pipe(errPipe) == 0);
  50. pid_t pid;
  51. switch( pid = fork() )
  52. {
  53. case -1:
  54. {
  55. release_assert(0 && "Fork failed");
  56. break;
  57. }
  58. case 0: // child
  59. {
  60. release_assert(dup2(parentToChild[READ_FD ], STDIN_FILENO ) != -1);
  61. release_assert(dup2(childToParent[WRITE_FD], STDOUT_FILENO) != -1);
  62. if(includeStdErr)
  63. {
  64. release_assert(dup2(childToParent[WRITE_FD], STDERR_FILENO) != -1);
  65. }
  66. else
  67. {
  68. int devNull = open("/dev/null", O_WRONLY);
  69. release_assert(dup2(devNull, STDERR_FILENO) != -1);
  70. }
  71. // unused
  72. release_assert(close(parentToChild[WRITE_FD]) == 0);
  73. release_assert(close(childToParent[READ_FD ]) == 0);
  74. release_assert(close(errPipe[READ_FD]) == 0);
  75. const char* command = allArgs[0];
  76. execvp(command, allArgs);
  77. char err = 1;
  78. ssize_t result = write(errPipe[WRITE_FD], &err, 1);
  79. release_assert(result != -1);
  80. close(errPipe[WRITE_FD]);
  81. close(parentToChild[READ_FD]);
  82. close(childToParent[WRITE_FD]);
  83. exit(0);
  84. }
  85. default: // parent
  86. {
  87. // unused
  88. release_assert(close(parentToChild[READ_FD]) == 0);
  89. release_assert(close(childToParent[WRITE_FD]) == 0);
  90. release_assert(close(errPipe[WRITE_FD]) == 0);
  91. while(1)
  92. {
  93. ssize_t bytesRead = 0;
  94. switch(bytesRead = read(childToParent[READ_FD], buffer, bufferSize))
  95. {
  96. case 0: // End-of-File, or non-blocking read.
  97. {
  98. int status = 0;
  99. release_assert(waitpid(pid, &status, 0) == pid);
  100. // done with these now
  101. release_assert(close(parentToChild[WRITE_FD]) == 0);
  102. release_assert(close(childToParent[READ_FD]) == 0);
  103. char errChar = 0;
  104. ssize_t result = read(errPipe[READ_FD], &errChar, 1);
  105. release_assert(result != -1);
  106. close(errPipe[READ_FD]);
  107. if(errChar)
  108. {
  109. free(dataReadFromChild);
  110. return COMMAND_NOT_FOUND;
  111. }
  112. // free any un-needed memory with realloc + add a null terminator for convenience
  113. dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildUsed + 1);
  114. dataReadFromChild[dataReadFromChildUsed] = '\0';
  115. if(stdOut != NULL)
  116. *stdOut = dataReadFromChild;
  117. else
  118. free(dataReadFromChild);
  119. if(stdOutByteCount != NULL)
  120. *stdOutByteCount = dataReadFromChildUsed;
  121. if(returnCode != NULL)
  122. *returnCode = WEXITSTATUS(status);
  123. return COMMAND_RAN_OK;
  124. }
  125. case -1:
  126. {
  127. release_assert(0 && "read() failed");
  128. break;
  129. }
  130. default:
  131. {
  132. if(dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize)
  133. {
  134. dataReadFromChildSize += dataReadFromChildDefaultSize;
  135. dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildSize);
  136. }
  137. memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead);
  138. dataReadFromChildUsed += bytesRead;
  139. break;
  140. }
  141. }
  142. }
  143. }
  144. }
  145. }
  146. int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...)
  147. {
  148. va_list vl;
  149. va_start(vl, command);
  150. char* currArg = NULL;
  151. int allArgsInitialSize = 16;
  152. int allArgsSize = allArgsInitialSize;
  153. char** allArgs = (char**)malloc(sizeof(char*) * allArgsSize);
  154. allArgs[0] = command;
  155. int i = 1;
  156. do
  157. {
  158. currArg = va_arg(vl, char*);
  159. allArgs[i] = currArg;
  160. i++;
  161. if(i >= allArgsSize)
  162. {
  163. allArgsSize += allArgsInitialSize;
  164. allArgs = (char**)realloc(allArgs, sizeof(char*) * allArgsSize);
  165. }
  166. } while(currArg != NULL);
  167. va_end(vl);
  168. int retval = runCommandArray(stdOut, stdOutByteCount, returnCode, includeStdErr, allArgs);
  169. free(allArgs);
  170. return retval;
  171. }
  172. #endif //SIMPLE_EXEC_IMPLEMENTATION