1 // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at",
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License
14 
15 #define CMDLINERUNNER_EXPORTS
16 
17 #include <windows.h>
18 #include <tchar.h>
19 #include <strsafe.h>
20 #include "cmdlinerunner.h"
21 
22 #define LINESIZE 1024
23 
24 #ifdef WITH_DEBUG
25 #include <stdio.h>
26 #define DEBUGV(...) do { fprintf(stderr, __VA_ARG__); } while(0)
27 #else
28 #define DEBUGV(...)
29 #endif
30 
31 struct proc_instance_s {
32     PROCESS_INFORMATION child_process;
33     HANDLE pipe_server_handle;
34     HANDLE pipe_client_handle;
35 };
36 
37 #ifdef WITH_DEBUG
print_last_error(void)38 static void print_last_error(void)
39 {
40     DWORD dw;
41     TCHAR errmsg[LINESIZE];
42     dw = GetLastError();
43 
44     FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
45         NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
46         errmsg, sizeof(errmsg) - 1, NULL );
47     DEBUGV("error %d: %s\n", dw, errmsg);
48 }
49 #define PRINT_LAST_ERROR() print_last_error()
50 #else
51 #define PRINT_LAST_ERROR()
52 #endif
53 
proc_instance_allocate(void)54 static proc_instance_t *proc_instance_allocate(void)
55 {
56     return (proc_instance_t*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(proc_instance_t));
57 }
58 
proc_instance_free(proc_instance_t * instance)59 static void proc_instance_free(proc_instance_t *instance)
60 {
61     if (instance->pipe_server_handle) {
62         CloseHandle(instance->pipe_server_handle);
63     }
64     if (instance->pipe_client_handle) {
65         CloseHandle(instance->pipe_client_handle);
66     }
67     if (instance->child_process.hProcess) {
68         TerminateProcess(instance->child_process.hProcess, 1);
69         CloseHandle(instance->child_process.hProcess);
70         CloseHandle(instance->child_process.hThread);
71     }
72     HeapFree(GetProcessHeap(), 0, instance);
73 }
74 
proc_end(proc_instance_t * inst)75 void proc_end(proc_instance_t *inst)
76 {
77     if (inst == NULL) {
78         return;
79     }
80     proc_instance_free(inst);
81 }
82 
proc_start(LPCTSTR cmdline,LPCTSTR workdir)83 CMDLINERUNNER_API proc_instance_t * proc_start(LPCTSTR cmdline, LPCTSTR workdir)
84 {
85     proc_instance_t *inst = proc_instance_allocate();
86     if (inst == NULL) {
87         return NULL;
88     }
89 
90     SECURITY_ATTRIBUTES sec_attr = {
91         .nLength = sizeof(SECURITY_ATTRIBUTES),
92         .bInheritHandle = TRUE,
93         .lpSecurityDescriptor = NULL
94     };
95 
96     LPCTSTR pipename = TEXT("\\\\.\\pipe\\cmdlinerunner_pipe");
97 
98     inst->pipe_server_handle = CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX,
99             PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024 * 16, 1024 * 16,
100             NMPWAIT_WAIT_FOREVER, &sec_attr);
101     if (inst->pipe_server_handle == INVALID_HANDLE_VALUE) {
102         DEBUGV("inst->pipe_server_handle == INVALID_HANDLE_VALUE\n");
103         goto error;
104     }
105 
106     inst->pipe_client_handle = CreateFile(pipename, GENERIC_WRITE | GENERIC_READ,
107             0, &sec_attr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
108     if (inst->pipe_client_handle == INVALID_HANDLE_VALUE) {
109         DEBUGV("inst->pipe_client_handle == INVALID_HANDLE_VALUE\n");
110         goto error;
111     }
112 
113     DWORD new_mode = PIPE_READMODE_BYTE | PIPE_NOWAIT;
114     if (!SetNamedPipeHandleState(inst->pipe_server_handle, &new_mode, NULL,
115             NULL)) {
116         DEBUGV("SetNamedPipeHandleState failed\n");
117         goto error;
118     }
119 
120     if (!SetHandleInformation(inst->pipe_server_handle, HANDLE_FLAG_INHERIT, 0)) {
121         DEBUGV("SetHandleInformation failed\n");
122         goto error;
123     }
124 
125     if (!SetHandleInformation(inst->pipe_client_handle, HANDLE_FLAG_INHERIT,
126             HANDLE_FLAG_INHERIT)) {
127         DEBUGV("SetHandleInformation failed\n");
128         goto error;
129     }
130 
131     STARTUPINFO siStartInfo = {
132         .cb = sizeof(STARTUPINFO),
133         .hStdError = inst->pipe_client_handle,
134         .hStdOutput = inst->pipe_client_handle,
135         .hStdInput = inst->pipe_client_handle,
136         .dwFlags = STARTF_USESTDHANDLES
137     };
138 
139     size_t workdir_len = 0;
140     StringCbLength(workdir, STRSAFE_MAX_CCH * sizeof(TCHAR), &workdir_len);
141     if (workdir_len == 0) {
142         workdir = NULL;
143     }
144 
145     TCHAR cmdline_tmp[LINESIZE];
146     StringCbCopy(cmdline_tmp, sizeof(cmdline_tmp), cmdline);
147     if (!CreateProcess(NULL, cmdline_tmp,
148     NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, workdir, &siStartInfo,
149             &inst->child_process)) {
150         DEBUGV("CreateProcess failed\n");
151         goto error;
152     }
153     return inst;
154 
155 error:
156     PRINT_LAST_ERROR();
157     proc_instance_free(inst);
158     return NULL;
159 }
160 
proc_get_exit_code(proc_instance_t * inst)161 int proc_get_exit_code(proc_instance_t *inst)
162 {
163     DWORD result;
164     if (!GetExitCodeProcess(inst->child_process.hProcess, &result)) {
165         return -2;
166     }
167     if (result == STILL_ACTIVE) {
168         return -1;
169     }
170     return (int) result;
171 }
172 
proc_get_output(proc_instance_t * inst,LPSTR dest,DWORD sz)173 DWORD proc_get_output(proc_instance_t *inst, LPSTR dest, DWORD sz)
174 {
175     DWORD read_bytes;
176     BOOL res = ReadFile(inst->pipe_server_handle, dest,
177             sz - 1, &read_bytes, NULL);
178     if (!res) {
179         if (GetLastError() == ERROR_NO_DATA) {
180             return 0;
181         } else {
182             PRINT_LAST_ERROR();
183             return 0;
184         }
185     }
186     dest[read_bytes] = 0;
187     return read_bytes;
188 }
189 
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)190 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved )
191 {
192     return TRUE;
193 }
194