aboutsummaryrefslogtreecommitdiff
path: root/src/Exec.cpp
blob: cc82897875de05fecdd1e56c218466da7d9e7928 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include "../include/Exec.hpp"
#include "../include/env.hpp"

#if OS_FAMILY == OS_FAMILY_POSIX
#include <sys/wait.h>
#endif

using namespace std;

const int BUFSIZE = 1024;

// TODO: Redirect stderr to 
namespace sibs
{
#if OS_FAMILY == OS_FAMILY_POSIX
    Result<ExecResult> exec(const _tinydir_char_t *cmd, bool print)
    {
        char buffer[BUFSIZE];
        std::string execStdout;
        FILE *pipe = popen(cmd, "r");
        if(!pipe)
            return Result<ExecResult>::Err("popen() failed");

        while(!feof(pipe))
        {
            if(fgets(buffer, BUFSIZE, pipe))
            {
                int bytesRead = strlen(buffer);
                execStdout.append(buffer, bytesRead);
                if(print)
                    printf("%.*s", bytesRead, buffer);
            }
        }

        int processCloseResult = pclose(pipe);
        if(WIFEXITED(processCloseResult))
        {
            int returned = WEXITSTATUS(processCloseResult);
            ExecResult execResult;
            execResult.execStdout = move(execStdout);
            execResult.exitCode = returned;
            return Result<ExecResult>::Ok(execResult);
        }
        else if(WIFSIGNALED(processCloseResult))
        {
            int signum = WSTOPSIG(processCloseResult);
            string errMsg = "Exited due to receiving signal ";
            errMsg += to_string(signum);
            return Result<ExecResult>::Err(errMsg);
        }
        else if(WIFSTOPPED(processCloseResult))
        {
            int signum = WSTOPSIG(processCloseResult);
            string errMsg = "Stopped due to receiving signal ";
            errMsg += to_string(signum);
            return Result<ExecResult>::Err(errMsg);
        }
        else
        {
            string errMsg = "exec unexpected error on pclose: ";
            errMsg += to_string(processCloseResult);
            return Result<ExecResult>::Err(errMsg);
        }
    }
#else
    // Currently stdout is read in text mode so \n is replaced with \r\n, should we read in binary mode instead?
    Result<ExecResult> exec(const _tinydir_char_t *cmd, bool print)
    {
        FileString cmdNonConst = cmd;
        std::string execStdout;

        SECURITY_ATTRIBUTES saAttr;
        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        saAttr.bInheritHandle = TRUE;
        saAttr.lpSecurityDescriptor = nullptr;

        HANDLE childReadHandle = nullptr;
        HANDLE childStdoutHandle = nullptr;

        if (!CreatePipe(&childReadHandle, &childStdoutHandle, &saAttr, 0))
        {
            string errMsg = "exec unexpected error: ";
            errMsg += toUtf8(getLastErrorAsString());
            return Result<ExecResult>::Err(errMsg);
        }

        if (!SetHandleInformation(childReadHandle, HANDLE_FLAG_INHERIT, 0))
            goto cleanupAndExit;

        PROCESS_INFORMATION piProcInfo;
        ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));

        STARTUPINFO siStartInfo;
        ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
        siStartInfo.cb = sizeof(STARTUPINFO);
        siStartInfo.hStdError = nullptr;
        siStartInfo.hStdOutput = childStdoutHandle;
        siStartInfo.hStdInput = nullptr;
        siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

        DWORD exitCode;

        if (!CreateProcessW(nullptr, (LPWSTR)cmdNonConst.data(), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo))
            goto cleanupAndExit;

        CloseHandle(childStdoutHandle);
        childStdoutHandle = nullptr;
        DWORD bytesRead;
        CHAR buffer[BUFSIZE];
        while (true)
        {
            BOOL bSuccess = ReadFile(childReadHandle, buffer, BUFSIZE, &bytesRead, nullptr);
            if (!bSuccess || bytesRead == 0)
                break;

            execStdout.append(buffer, bytesRead);
            if (print)
                printf("%.*s", bytesRead, buffer);
        }

        WaitForSingleObject(piProcInfo.hProcess, INFINITE);
        GetExitCodeProcess(piProcInfo.hProcess, &exitCode);
        CloseHandle(piProcInfo.hProcess);
        CloseHandle(piProcInfo.hThread);

        {
            ExecResult execResult;
            execResult.execStdout = move(execStdout);
            execResult.exitCode = exitCode;
            CloseHandle(childReadHandle);
            return Result<ExecResult>::Ok(execResult);
        }

    cleanupAndExit:
        string errMsg = "exec unexpected error: ";
        errMsg += toUtf8(getLastErrorAsString());
        CloseHandle(childReadHandle);
        CloseHandle(childStdoutHandle);
        return Result<ExecResult>::Err(errMsg);
    }
#endif
    Result<ExecResult> exec(const FileString &cmd, bool print)
    {
        return exec(cmd.c_str(), print);
    }
}