219 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			219 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								// copied from: https://github.com/wheybags/simple_exec/blob/5a74c507c4ce1b2bb166177ead4cca7cfa23cb35/simple_exec.h
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// simple_exec.h, single header library to run external programs + retrieve their status code and output (unix only for now)
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// do this:
							 | 
						||
| 
								 | 
							
								// #define SIMPLE_EXEC_IMPLEMENTATION
							 | 
						||
| 
								 | 
							
								//   before you include this file in *one* C or C++ file to create the implementation.
							 | 
						||
| 
								 | 
							
								// i.e. it should look like this:
							 | 
						||
| 
								 | 
							
								// #define SIMPLE_EXEC_IMPLEMENTATION
							 | 
						||
| 
								 | 
							
								// #include "simple_exec.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef SIMPLE_EXEC_H
							 | 
						||
| 
								 | 
							
								#define SIMPLE_EXEC_H
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...);
							 | 
						||
| 
								 | 
							
								int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#endif // SIMPLE_EXEC_H
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef SIMPLE_EXEC_IMPLEMENTATION
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <stdio.h>
							 | 
						||
| 
								 | 
							
								#include <stdlib.h>
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								#include <unistd.h>
							 | 
						||
| 
								 | 
							
								#include <assert.h>
							 | 
						||
| 
								 | 
							
								#include <sys/wait.h>
							 | 
						||
| 
								 | 
							
								#include <stdarg.h>
							 | 
						||
| 
								 | 
							
								#include <fcntl.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define release_assert(exp) { if (!(exp)) { abort(); } }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum PIPE_FILE_DESCRIPTORS
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  READ_FD  = 0,
							 | 
						||
| 
								 | 
							
								  WRITE_FD = 1
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum RUN_COMMAND_ERROR
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    COMMAND_RAN_OK = 0,
							 | 
						||
| 
								 | 
							
								    COMMAND_NOT_FOUND = 1
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // adapted from: https://stackoverflow.com/a/479103
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int bufferSize = 256;
							 | 
						||
| 
								 | 
							
								    char buffer[bufferSize + 1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int dataReadFromChildDefaultSize = bufferSize * 5;
							 | 
						||
| 
								 | 
							
								    int dataReadFromChildSize = dataReadFromChildDefaultSize;
							 | 
						||
| 
								 | 
							
								    int dataReadFromChildUsed = 0;
							 | 
						||
| 
								 | 
							
								    char* dataReadFromChild = (char*)malloc(dataReadFromChildSize);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int parentToChild[2];
							 | 
						||
| 
								 | 
							
								    release_assert(pipe(parentToChild) == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int childToParent[2];
							 | 
						||
| 
								 | 
							
								    release_assert(pipe(childToParent) == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int errPipe[2];
							 | 
						||
| 
								 | 
							
								    release_assert(pipe(errPipe) == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    pid_t pid;
							 | 
						||
| 
								 | 
							
								    switch( pid = fork() )
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        case -1:
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            release_assert(0 && "Fork failed");
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        case 0: // child
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            release_assert(dup2(parentToChild[READ_FD ], STDIN_FILENO ) != -1);
							 | 
						||
| 
								 | 
							
								            release_assert(dup2(childToParent[WRITE_FD], STDOUT_FILENO) != -1);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if(includeStdErr)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                release_assert(dup2(childToParent[WRITE_FD], STDERR_FILENO) != -1);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                int devNull = open("/dev/null", O_WRONLY);
							 | 
						||
| 
								 | 
							
								                release_assert(dup2(devNull, STDERR_FILENO) != -1);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // unused
							 | 
						||
| 
								 | 
							
								            release_assert(close(parentToChild[WRITE_FD]) == 0);
							 | 
						||
| 
								 | 
							
								            release_assert(close(childToParent[READ_FD ]) == 0);
							 | 
						||
| 
								 | 
							
								            release_assert(close(errPipe[READ_FD]) == 0);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            const char* command = allArgs[0];
							 | 
						||
| 
								 | 
							
								            execvp(command, allArgs);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            char err = 1;
							 | 
						||
| 
								 | 
							
								            ssize_t result = write(errPipe[WRITE_FD], &err, 1);
							 | 
						||
| 
								 | 
							
								            release_assert(result != -1);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            close(errPipe[WRITE_FD]);
							 | 
						||
| 
								 | 
							
								            close(parentToChild[READ_FD]);
							 | 
						||
| 
								 | 
							
								            close(childToParent[WRITE_FD]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            exit(0);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        default: // parent
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            // unused
							 | 
						||
| 
								 | 
							
								            release_assert(close(parentToChild[READ_FD]) == 0);
							 | 
						||
| 
								 | 
							
								            release_assert(close(childToParent[WRITE_FD]) == 0);
							 | 
						||
| 
								 | 
							
								            release_assert(close(errPipe[WRITE_FD]) == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            while(1)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                ssize_t bytesRead = 0;
							 | 
						||
| 
								 | 
							
								                switch(bytesRead = read(childToParent[READ_FD], buffer, bufferSize))
							 | 
						||
| 
								 | 
							
								                {
							 | 
						||
| 
								 | 
							
								                    case 0: // End-of-File, or non-blocking read.
							 | 
						||
| 
								 | 
							
								                    {
							 | 
						||
| 
								 | 
							
								                        int status = 0;
							 | 
						||
| 
								 | 
							
								                        release_assert(waitpid(pid, &status, 0) == pid);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        // done with these now
							 | 
						||
| 
								 | 
							
								                        release_assert(close(parentToChild[WRITE_FD]) == 0);
							 | 
						||
| 
								 | 
							
								                        release_assert(close(childToParent[READ_FD]) == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        char errChar = 0;
							 | 
						||
| 
								 | 
							
								                        ssize_t result = read(errPipe[READ_FD], &errChar, 1);
							 | 
						||
| 
								 | 
							
								                        release_assert(result != -1);
							 | 
						||
| 
								 | 
							
								                        close(errPipe[READ_FD]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        if(errChar)
							 | 
						||
| 
								 | 
							
								                        {
							 | 
						||
| 
								 | 
							
								                            free(dataReadFromChild); 
							 | 
						||
| 
								 | 
							
								                            return COMMAND_NOT_FOUND;
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        
							 | 
						||
| 
								 | 
							
								                        // free any un-needed memory with realloc + add a null terminator for convenience
							 | 
						||
| 
								 | 
							
								                        dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildUsed + 1);
							 | 
						||
| 
								 | 
							
								                        dataReadFromChild[dataReadFromChildUsed] = '\0';
							 | 
						||
| 
								 | 
							
								                        
							 | 
						||
| 
								 | 
							
								                        if(stdOut != NULL)
							 | 
						||
| 
								 | 
							
								                            *stdOut = dataReadFromChild;
							 | 
						||
| 
								 | 
							
								                        else
							 | 
						||
| 
								 | 
							
								                            free(dataReadFromChild);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        if(stdOutByteCount != NULL)
							 | 
						||
| 
								 | 
							
								                            *stdOutByteCount = dataReadFromChildUsed;
							 | 
						||
| 
								 | 
							
								                        if(returnCode != NULL)
							 | 
						||
| 
								 | 
							
								                            *returnCode = WEXITSTATUS(status);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        return COMMAND_RAN_OK;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    case -1:
							 | 
						||
| 
								 | 
							
								                    {
							 | 
						||
| 
								 | 
							
								                        release_assert(0 && "read() failed");
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    default:
							 | 
						||
| 
								 | 
							
								                    {
							 | 
						||
| 
								 | 
							
								                        if(dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize)
							 | 
						||
| 
								 | 
							
								                        {
							 | 
						||
| 
								 | 
							
								                            dataReadFromChildSize += dataReadFromChildDefaultSize;
							 | 
						||
| 
								 | 
							
								                            dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildSize);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead);
							 | 
						||
| 
								 | 
							
								                        dataReadFromChildUsed += bytesRead;
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    va_list vl;
							 | 
						||
| 
								 | 
							
								    va_start(vl, command);
							 | 
						||
| 
								 | 
							
								      
							 | 
						||
| 
								 | 
							
								    char* currArg = NULL;
							 | 
						||
| 
								 | 
							
								      
							 | 
						||
| 
								 | 
							
								    int allArgsInitialSize = 16;
							 | 
						||
| 
								 | 
							
								    int allArgsSize = allArgsInitialSize;
							 | 
						||
| 
								 | 
							
								    char** allArgs = (char**)malloc(sizeof(char*) * allArgsSize);
							 | 
						||
| 
								 | 
							
								    allArgs[0] = command;
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								    int i = 1;
							 | 
						||
| 
								 | 
							
								    do
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        currArg = va_arg(vl, char*);
							 | 
						||
| 
								 | 
							
								        allArgs[i] = currArg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        i++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if(i >= allArgsSize)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            allArgsSize += allArgsInitialSize;
							 | 
						||
| 
								 | 
							
								            allArgs = (char**)realloc(allArgs, sizeof(char*) * allArgsSize);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    } while(currArg != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    va_end(vl);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int retval = runCommandArray(stdOut, stdOutByteCount, returnCode, includeStdErr, allArgs);
							 | 
						||
| 
								 | 
							
								    free(allArgs);
							 | 
						||
| 
								 | 
							
								    return retval;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#endif //SIMPLE_EXEC_IMPLEMENTATION
							 |