2
0

simple_exec.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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(x) do { int __release_assert_tmp__ = (x); assert(__release_assert_tmp__); } while(0)
  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. }
  57. case 0: // child
  58. {
  59. release_assert(dup2(parentToChild[READ_FD ], STDIN_FILENO ) != -1);
  60. release_assert(dup2(childToParent[WRITE_FD], STDOUT_FILENO) != -1);
  61. if(includeStdErr)
  62. {
  63. release_assert(dup2(childToParent[WRITE_FD], STDERR_FILENO) != -1);
  64. }
  65. else
  66. {
  67. int devNull = open("/dev/null", O_WRONLY);
  68. release_assert(dup2(devNull, STDERR_FILENO) != -1);
  69. }
  70. // unused
  71. release_assert(close(parentToChild[WRITE_FD]) == 0);
  72. release_assert(close(childToParent[READ_FD ]) == 0);
  73. release_assert(close(errPipe[READ_FD]) == 0);
  74. const char* command = allArgs[0];
  75. execvp(command, allArgs);
  76. char err = 1;
  77. write(errPipe[WRITE_FD], &err, 1);
  78. close(errPipe[WRITE_FD]);
  79. close(parentToChild[READ_FD]);
  80. close(childToParent[WRITE_FD]);
  81. exit(0);
  82. }
  83. default: // parent
  84. {
  85. // unused
  86. release_assert(close(parentToChild[READ_FD]) == 0);
  87. release_assert(close(childToParent[WRITE_FD]) == 0);
  88. release_assert(close(errPipe[WRITE_FD]) == 0);
  89. while(1)
  90. {
  91. ssize_t bytesRead = 0;
  92. switch(bytesRead = read(childToParent[READ_FD], buffer, bufferSize))
  93. {
  94. case 0: // End-of-File, or non-blocking read.
  95. {
  96. int status = 0;
  97. release_assert(waitpid(pid, &status, 0) == pid);
  98. // done with these now
  99. release_assert(close(parentToChild[WRITE_FD]) == 0);
  100. release_assert(close(childToParent[READ_FD]) == 0);
  101. char errChar = 0;
  102. read(errPipe[READ_FD], &errChar, 1);
  103. close(errPipe[READ_FD]);
  104. if(errChar)
  105. {
  106. free(dataReadFromChild);
  107. return COMMAND_NOT_FOUND;
  108. }
  109. // free any un-needed memory with realloc + add a null terminator for convenience
  110. dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildUsed + 1);
  111. dataReadFromChild[dataReadFromChildUsed] = '\0';
  112. if(stdOut != NULL)
  113. *stdOut = dataReadFromChild;
  114. else
  115. free(dataReadFromChild);
  116. if(stdOutByteCount != NULL)
  117. *stdOutByteCount = dataReadFromChildUsed;
  118. if(returnCode != NULL)
  119. *returnCode = WEXITSTATUS(status);
  120. return COMMAND_RAN_OK;
  121. }
  122. case -1:
  123. {
  124. release_assert(0 && "read() failed");
  125. }
  126. default:
  127. {
  128. if(dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize)
  129. {
  130. dataReadFromChildSize += dataReadFromChildDefaultSize;
  131. dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildSize);
  132. }
  133. memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead);
  134. dataReadFromChildUsed += bytesRead;
  135. break;
  136. }
  137. }
  138. }
  139. }
  140. }
  141. }
  142. int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...)
  143. {
  144. va_list vl;
  145. va_start(vl, command);
  146. char* currArg = NULL;
  147. int allArgsInitialSize = 16;
  148. int allArgsSize = allArgsInitialSize;
  149. char** allArgs = (char**)malloc(sizeof(char*) * allArgsSize);
  150. allArgs[0] = command;
  151. int i = 1;
  152. do
  153. {
  154. currArg = va_arg(vl, char*);
  155. allArgs[i] = currArg;
  156. i++;
  157. if(i >= allArgsSize)
  158. {
  159. allArgsSize += allArgsInitialSize;
  160. allArgs = (char**)realloc(allArgs, sizeof(char*) * allArgsSize);
  161. }
  162. } while(currArg != NULL);
  163. va_end(vl);
  164. int retval = runCommandArray(stdOut, stdOutByteCount, returnCode, includeStdErr, allArgs);
  165. free(allArgs);
  166. return retval;
  167. }
  168. #endif //SIMPLE_EXEC_IMPLEMENTATION