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, &copy, 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