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) 24static 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) 34static 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} 38static 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) 50static 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} 60static 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 */ 68static 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 */ 77static 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 */ 98static 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 */ 112static 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 */ 119static 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 */ 126static 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 */ 135int 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 */ 148int 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 */ 213int 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 */ 235int 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 */ 279static void proc_read_fd_to_buffer(int fd, buffer *b) { 280 ssize_t s; 281 282 for (;;) { 283 buffer_string_prepare_append(b, 1024); 284 if ((s = read(fd, (void *)(b->ptr + buffer_string_length(b)), buffer_string_space(b))) <= 0) { 285 break; 286 } 287 buffer_commit(b, s); 288 } 289} 290/* }}} */ 291/* {{{ proc_open_buffer */ 292int proc_open_buffer(const char *command, buffer *in, buffer *out, buffer *err) { 293 proc_handler_t proc; 294 295 if (proc_open(&proc, command) != 0) { 296 return -1; 297 } 298 299 if (in) { 300 if (write(proc.in.fd, CONST_BUF_LEN(in)) < 0) { 301 perror("error writing pipe"); 302 return -1; 303 } 304 } 305 pipe_close(&proc.in); 306 307 if (out) { 308 proc_read_fd_to_buffer(proc.out.fd, out); 309 } 310 pipe_close(&proc.out); 311 312 if (err) { 313 proc_read_fd_to_buffer(proc.err.fd, err); 314 } else { 315 buffer *tmp = buffer_init(); 316 proc_read_fd_to_buffer(proc.err.fd, tmp); 317 if (!buffer_string_is_empty(tmp) && write(2, CONST_BUF_LEN(tmp)) < 0) { 318 perror("error writing pipe"); 319 buffer_free(tmp); 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 334int 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