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