1 #include "proc_open.h"
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <ctype.h>
6 #include <string.h>
7 #include <errno.h>
8
9 #ifdef WIN32
10 # include <io.h>
11 # include <fcntl.h>
12 #else
13 # include <sys/wait.h>
14 # include <unistd.h>
15 #endif
16
17
18 #ifdef WIN32
19 /* {{{ win32 stuff */
20 # define SHELLENV "ComSpec"
21 # define SECURITY_DC , SECURITY_ATTRIBUTES *security
22 # define SECURITY_CC , security
23 # define pipe(pair) (CreatePipe(&pair[0], &pair[1], security, 2048L) ? 0 : -1)
dup_handle(HANDLE src,BOOL inherit,BOOL closeorig)24 static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig)
25 {
26 HANDLE copy, self = GetCurrentProcess();
27
28 if (!DuplicateHandle(self, src, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS |
29 (closeorig ? DUPLICATE_CLOSE_SOURCE : 0)))
30 return NULL;
31 return copy;
32 }
33 # define close_descriptor(fd) CloseHandle(fd)
pipe_close_parent(pipe_t * p)34 static void pipe_close_parent(pipe_t *p) {
35 /* don't let the child inherit the parent side of the pipe */
36 p->parent = dup_handle(p->parent, FALSE, TRUE);
37 }
pipe_close_child(pipe_t * p)38 static void pipe_close_child(pipe_t *p) {
39 close_descriptor(p->child);
40 p->fd = _open_osfhandle((long)p->parent,
41 (p->fd == 0 ? O_RDONLY : O_WRONLY)|O_BINARY);
42 }
43 /* }}} */
44 #else /* WIN32 */
45 /* {{{ unix way */
46 # define SHELLENV "SHELL"
47 # define SECURITY_DC
48 # define SECURITY_CC
49 # define close_descriptor(fd) close(fd)
pipe_close_parent(pipe_t * p)50 static void pipe_close_parent(pipe_t *p) {
51 /* don't close stdin */
52 close_descriptor(p->parent);
53 if (dup2(p->child, p->fd) != p->fd) {
54 perror("pipe_child dup2");
55 } else {
56 close_descriptor(p->child);
57 p->child = p->fd;
58 }
59 }
pipe_close_child(pipe_t * p)60 static void pipe_close_child(pipe_t *p) {
61 close_descriptor(p->child);
62 p->fd = p->parent;
63 }
64 /* }}} */
65 #endif /* WIN32 */
66
67 /* {{{ pipe_close */
pipe_close(pipe_t * p)68 static void pipe_close(pipe_t *p) {
69 close_descriptor(p->parent);
70 close_descriptor(p->child);
71 #ifdef WIN32
72 close(p->fd);
73 #endif
74 }
75 /* }}} */
76 /* {{{ pipe_open */
pipe_open(pipe_t * p,int fd SECURITY_DC)77 static int pipe_open(pipe_t *p, int fd SECURITY_DC) {
78 descriptor_t newpipe[2];
79
80 if (0 != pipe(newpipe)) {
81 fprintf(stderr, "can't open pipe");
82 return -1;
83 }
84 if (0 == fd) {
85 p->parent = newpipe[1]; /* write */
86 p->child = newpipe[0]; /* read */
87 } else {
88 p->parent = newpipe[0]; /* read */
89 p->child = newpipe[1]; /* write */
90 }
91 p->fd = fd;
92
93 return 0;
94 }
95 /* }}} */
96
97 /* {{{ proc_open_pipes */
proc_open_pipes(proc_handler_t * proc SECURITY_DC)98 static int proc_open_pipes(proc_handler_t *proc SECURITY_DC) {
99 if (pipe_open(&(proc->in), 0 SECURITY_CC) != 0) {
100 return -1;
101 }
102 if (pipe_open(&(proc->out), 1 SECURITY_CC) != 0) {
103 return -1;
104 }
105 if (pipe_open(&(proc->err), 2 SECURITY_CC) != 0) {
106 return -1;
107 }
108 return 0;
109 }
110 /* }}} */
111 /* {{{ proc_close_pipes */
proc_close_pipes(proc_handler_t * proc)112 static void proc_close_pipes(proc_handler_t *proc) {
113 pipe_close(&proc->in);
114 pipe_close(&proc->out);
115 pipe_close(&proc->err);
116 }
117 /* }}} */
118 /* {{{ proc_close_parents */
proc_close_parents(proc_handler_t * proc)119 static void proc_close_parents(proc_handler_t *proc) {
120 pipe_close_parent(&proc->in);
121 pipe_close_parent(&proc->out);
122 pipe_close_parent(&proc->err);
123 }
124 /* }}} */
125 /* {{{ proc_close_childs */
proc_close_childs(proc_handler_t * proc)126 static void proc_close_childs(proc_handler_t *proc) {
127 pipe_close_child(&proc->in);
128 pipe_close_child(&proc->out);
129 pipe_close_child(&proc->err);
130 }
131 /* }}} */
132
133 #ifdef WIN32
134 /* {{{ proc_close */
proc_close(proc_handler_t * proc)135 int proc_close(proc_handler_t *proc) {
136 proc_pid_t child = proc->child;
137 DWORD wstatus;
138
139 proc_close_pipes(proc);
140 WaitForSingleObject(child, INFINITE);
141 GetExitCodeProcess(child, &wstatus);
142 CloseHandle(child);
143
144 return wstatus;
145 }
146 /* }}} */
147 /* {{{ proc_open */
proc_open(proc_handler_t * proc,const char * command)148 int proc_open(proc_handler_t *proc, const char *command) {
149 PROCESS_INFORMATION pi;
150 STARTUPINFO si;
151 BOOL procok;
152 SECURITY_ATTRIBUTES security;
153 const char *shell = NULL;
154 const char *windir = NULL;
155 buffer *cmdline;
156
157 if (NULL == (shell = getenv(SHELLENV)) &&
158 NULL == (windir = getenv("SystemRoot")) &&
159 NULL == (windir = getenv("windir"))) {
160 fprintf(stderr, "One of %s,%%SystemRoot,%%windir is required", SHELLENV);
161 return -1;
162 }
163
164 /* we use this to allow the child to inherit handles */
165 memset(&security, 0, sizeof(security));
166 security.nLength = sizeof(security);
167 security.bInheritHandle = TRUE;
168 security.lpSecurityDescriptor = NULL;
169
170 if (proc_open_pipes(proc, &security) != 0) {
171 return -1;
172 }
173 proc_close_parents(proc);
174
175 memset(&si, 0, sizeof(si));
176 si.cb = sizeof(si);
177 si.dwFlags = STARTF_USESTDHANDLES;
178 si.hStdInput = proc->in.child;
179 si.hStdOutput = proc->out.child;
180 si.hStdError = proc->err.child;
181
182 memset(&pi, 0, sizeof(pi));
183
184 cmdline = buffer_init();
185 if (shell) {
186 buffer_append_string(cmdline, shell);
187 } else {
188 buffer_append_string(cmdline, windir);
189 buffer_append_string_len(cmdline, CONST_STR_LEN("\\system32\\cmd.exe"));
190 }
191 buffer_append_string_len(cmdline, CONST_STR_LEN(" /c "));
192 buffer_append_string(cmdline, command);
193 procok = CreateProcess(NULL, cmdline->ptr, &security, &security, TRUE,
194 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
195
196 if (FALSE == procok) {
197 fprintf(stderr, "failed to CreateProcess: %s", cmdline->ptr);
198 buffer_free(cmdline);
199 return -1;
200 }
201 buffer_free(cmdline);
202
203 proc->child = pi.hProcess;
204 CloseHandle(pi.hThread);
205
206 proc_close_childs(proc);
207
208 return 0;
209 }
210 /* }}} */
211 #else /* WIN32 */
212 /* {{{ proc_close */
proc_close(proc_handler_t * proc)213 int proc_close(proc_handler_t *proc) {
214 pid_t child = proc->child;
215 int wstatus;
216 pid_t wait_pid;
217
218 proc_close_pipes(proc);
219
220 do {
221 wait_pid = waitpid(child, &wstatus, 0);
222 } while (wait_pid == -1 && errno == EINTR);
223
224 if (wait_pid == -1) {
225 return -1;
226 } else {
227 if (WIFEXITED(wstatus))
228 wstatus = WEXITSTATUS(wstatus);
229 }
230
231 return wstatus;
232 }
233 /* }}} */
234 /* {{{ proc_open */
proc_open(proc_handler_t * proc,const char * command)235 int proc_open(proc_handler_t *proc, const char *command) {
236 pid_t child;
237 const char *shell;
238
239 if (NULL == (shell = getenv(SHELLENV))) {
240 shell = "/bin/sh";
241 }
242
243 if (proc_open_pipes(proc) != 0) {
244 return -1;
245 }
246
247 /* the unix way */
248
249 child = fork();
250
251 if (child == 0) {
252 /* this is the child process */
253
254 /* close those descriptors that we just opened for the parent stuff,
255 * dup new descriptors into required descriptors and close the original
256 * cruft
257 */
258 proc_close_parents(proc);
259
260 execl(shell, shell, "-c", command, (char *)NULL);
261 fprintf(stderr, "failed to execute shell: %s -c %s: %s\n", shell, command, strerror(errno));
262 _exit(127);
263
264 } else if (child < 0) {
265 fprintf(stderr, "failed to forking");
266 proc_close(proc);
267 return -1;
268
269 } else {
270 proc->child = child;
271 proc_close_childs(proc);
272 return 0;
273 }
274 }
275 /* }}} */
276 #endif /* WIN32 */
277
278 /* {{{ proc_read_fd_to_buffer */
proc_read_fd_to_buffer(int fd,buffer * b)279 static void proc_read_fd_to_buffer(int fd, buffer *b) {
280 ssize_t s;
281
282 for (;;) {
283 buffer_prepare_append(b, 512);
284 if ((s = read(fd, (void *)(b->ptr + b->used), 512 - 1)) <= 0) {
285 break;
286 }
287 b->used += s;
288 }
289 b->ptr[b->used] = '\0';
290 }
291 /* }}} */
292 /* {{{ proc_open_buffer */
proc_open_buffer(const char * command,buffer * in,buffer * out,buffer * err)293 int proc_open_buffer(const char *command, buffer *in, buffer *out, buffer *err) {
294 proc_handler_t proc;
295
296 if (proc_open(&proc, command) != 0) {
297 return -1;
298 }
299
300 if (in) {
301 if (write(proc.in.fd, (void *)in->ptr, in->used) < 0) {
302 perror("error writing pipe");
303 return -1;
304 }
305 }
306 pipe_close(&proc.in);
307
308 if (out) {
309 proc_read_fd_to_buffer(proc.out.fd, out);
310 }
311 pipe_close(&proc.out);
312
313 if (err) {
314 proc_read_fd_to_buffer(proc.err.fd, err);
315 } else {
316 buffer *tmp = buffer_init();
317 proc_read_fd_to_buffer(proc.err.fd, tmp);
318 if (tmp->used > 0 && write(2, (void*)tmp->ptr, tmp->used) < 0) {
319 perror("error writing pipe");
320 return -1;
321 }
322 buffer_free(tmp);
323 }
324 pipe_close(&proc.err);
325
326 proc_close(&proc);
327
328 return 0;
329 }
330 /* }}} */
331
332 /* {{{ test */
333 #ifdef DEBUG_PROC_OPEN
main(void)334 int main(void) {
335 proc_handler_t proc;
336 buffer *in = buffer_init(), *out = buffer_init(), *err = buffer_init();
337 int wstatus;
338
339 #define FREE() do { \
340 buffer_free(in); \
341 buffer_free(out); \
342 buffer_free(err); \
343 } while (0)
344
345 #define RESET() do { \
346 buffer_reset(in); \
347 buffer_reset(out); \
348 buffer_reset(err); \
349 wstatus = proc_close(&proc); \
350 if (0&&wstatus != 0) { \
351 fprintf(stdout, "exitstatus %d\n", wstatus); \
352 return __LINE__ - 200; \
353 } \
354 } while (0)
355
356 #define ERROR_OUT() do { \
357 fprintf(stdout, "failed opening proc\n"); \
358 wstatus = proc_close(&proc); \
359 fprintf(stdout, "exitstatus %d\n", wstatus); \
360 FREE(); \
361 return __LINE__ - 300; \
362 } while (0)
363
364 #ifdef WIN32
365 #define CMD_CAT "pause"
366 #else
367 #define CMD_CAT "cat"
368 #endif
369
370 do {
371 fprintf(stdout, "test: echo 123 without read\n");
372 if (proc_open(&proc, "echo 321") != 0) {
373 ERROR_OUT();
374 }
375 close_descriptor(proc.in.parent);
376 close_descriptor(proc.out.parent);
377 close_descriptor(proc.err.parent);
378 RESET();
379
380 fprintf(stdout, "test: echo 321 with read\n"); fflush(stdout);
381 if (proc_open_buffer("echo 321", NULL, out, err) != 0) {
382 ERROR_OUT();
383 }
384 fprintf(stdout, "result: ->%s<-\n\n", out->ptr); fflush(stdout);
385 RESET();
386
387 fprintf(stdout, "test: echo 123 | " CMD_CAT "\n"); fflush(stdout);
388 buffer_copy_string_len(in, CONST_STR_LEN("123\n"));
389 if (proc_open_buffer(CMD_CAT, in, out, err) != 0) {
390 ERROR_OUT();
391 }
392 fprintf(stdout, "result: ->%s<-\n\n", out->ptr); fflush(stdout);
393 RESET();
394 } while (0);
395
396 #undef RESET
397 #undef ERROR_OUT
398
399 fprintf(stdout, "ok\n");
400
401 FREE();
402 return 0;
403 }
404 #endif /* DEBUG_PROC_OPEN */
405 /* }}} */
406
407