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