|
|
1.1 paf 1: /** @file
2: Parser: program executing for different OS-es.
3:
4: Copyright(c) 2000,2001 ArtLebedev Group(http://www.artlebedev.com)
1.17 paf 5: Author: Alexander Petrosyan <paf@design.ru>(http://paf.design.ru)
1.1 paf 6:
1.18 ! paf 7: $Id: pa_exec.C,v 1.17 2001/11/05 11:46:28 paf Exp $
! 8:
! 9:
! 10: @todo setrlimit
1.1 paf 11: */
12:
13: #include "pa_config_includes.h"
14:
15: #ifdef WIN32
16: # include <windows.h>
17: #else
18: # include <signal.h>
19: # include <sys/types.h>
20: # include <sys/wait.h>
21: #endif
22:
23: #include <stdio.h>
24: #include <errno.h>
25:
26: #include "pa_exec.h"
27: #include "pa_exception.h"
28: #include "pa_common.h"
29:
30:
31: #ifdef WIN32
32:
33: /// this func from http://www.ccas.ru/~posp/popov/spawn.htm
1.9 parser 34: static BOOL WINAPI CreateHiddenConsoleProcess(LPCTSTR szChildName,
1.1 paf 35: char *szEnv,
36: PROCESS_INFORMATION* ppi,
37: LPHANDLE phInWrite,
38: LPHANDLE phOutRead,
1.9 parser 39: LPHANDLE phErrRead)
1.1 paf 40: {
41: BOOL fCreated;
42: STARTUPINFO si;
43: SECURITY_ATTRIBUTES sa={0};
44: HANDLE hInRead;
45: HANDLE hOutWrite;
46: HANDLE hErrWrite;
47:
48: // Create pipes
49: // initialize security attributes for handle inheritance (for WinNT)
1.9 parser 50: sa.nLength=sizeof(sa);
51: sa.bInheritHandle=TRUE;
52: sa.lpSecurityDescriptor=NULL;
1.1 paf 53:
54: // create STDIN pipe
1.9 parser 55: if(!CreatePipe(&hInRead, phInWrite, &sa, 0))
1.1 paf 56: goto error;
57:
58: // create STDOUT pipe
1.9 parser 59: if(!CreatePipe(phOutRead, &hOutWrite, &sa, 0))
1.1 paf 60: goto error;
61:
62: // create STDERR pipe
1.9 parser 63: if(!CreatePipe(phErrRead, &hErrWrite, &sa, 0))
1.1 paf 64: goto error;
65:
66: // process startup information
1.9 parser 67: memset(&si, 0, sizeof(si));
68: si.cb=sizeof(si);
69: si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
1.1 paf 70: // child process' console must be hidden for Win95 compatibility
1.9 parser 71: si.wShowWindow=SW_HIDE;
1.1 paf 72: // assign "other" sides of pipes
1.9 parser 73: si.hStdInput=hInRead;
74: si.hStdOutput=hOutWrite;
75: si.hStdError=hErrWrite;
1.1 paf 76:
77: // Create a child process (suspended)
1.9 parser 78: fCreated=CreateProcess(NULL,
1.1 paf 79: (LPTSTR)szChildName,
80: NULL,
81: NULL,
82: TRUE,
1.9 parser 83: 0, //todo CREATE_NO_WINDOW,
1.1 paf 84: szEnv,
85: NULL,
86: &si,
1.9 parser 87: ppi);
1.1 paf 88:
1.9 parser 89: CloseHandle(hInRead);
90: CloseHandle(hOutWrite);
91: CloseHandle(hErrWrite);
1.1 paf 92:
1.9 parser 93: if(!fCreated)
1.1 paf 94: goto error;
95:
96: return TRUE;
97:
98: error:
1.9 parser 99: CloseHandle(*phInWrite);
100: CloseHandle(*phOutRead);
101: CloseHandle(*phErrRead);
1.1 paf 102:
103: return FALSE;
104: }
105:
1.2 paf 106: static void read_pipe(String& result, HANDLE hOutRead, const char *file_spec){
1.1 paf 107: while(true) {
108: char *buf=(char *)result.pool().malloc(MAX_STRING);
109: unsigned long size;
1.9 parser 110: ReadFile(hOutRead, buf, MAX_STRING, &size, NULL);
1.1 paf 111: if(!size)
112: break;
1.3 paf 113: result.APPEND_AS_IS(buf, size, file_spec, 0);
1.1 paf 114: }
115: }
116:
117:
118: static const char *buildCommand(Pool& pool,
119: const String& origin_string,
120: const char *file_spec_cstr, const Array *argv) {
1.8 parser 121: const char *result=file_spec_cstr;
1.4 paf 122: if(FILE *f=fopen(file_spec_cstr, "r")) {
1.1 paf 123: char buf[MAX_STRING];
124: size_t size=fread(buf, 1, MAX_STRING-1, f);
125: if(size>2) {
126: buf[size]=0;
127: if(strncmp(buf, "#!", 2)==0) {
1.4 paf 128: const char *begin=buf+2;
129: if(*begin==' ') // alx: were an old magic for some linux-es
130: begin++;
131: if(char *end=strchr(begin, '\n')) {
1.1 paf 132: String string(pool);
1.4 paf 133: string.APPEND_AS_IS(begin, end-begin,
1.1 paf 134: origin_string.origin().file, 0);
135: string << " " << file_spec_cstr;
136: if(argv)
137: for(int i=0; i<argv->size(); i++)
1.16 paf 138: string << argv->get_string(i)->cstr();
1.8 parser 139: result=string.cstr();
1.1 paf 140: }
141: }
142: }
143: fclose(f);
144: }
1.8 parser 145: if(argv) {
146: String buf(pool);
147: buf << result;
148: for(int i=0; i<argv->size(); i++) {
149: buf << " ";
150: buf << *argv->get_string(i);
151: }
152:
1.16 paf 153: result=buf.cstr();
1.8 parser 154: }
155:
156: return result;
1.1 paf 157: }
158:
159: #else
160:
161: static int execle_piped(const char *path,
1.8 parser 162: const char *arg1, const char *arg2,
163: const char *arg3, const char *arg4,
164: const char *arg5, const char *arg6,
165: const char *arg7, const char *arg8,
166: const char *arg9, const char *arg10,
1.1 paf 167: char * const env[],
168: int *pipe_in, int *pipe_out, int *pipe_err) {
169: int pid;
170: int in_fds[2];
171: int out_fds[2];
172: int err_fds[2];
173: int save_errno;
174:
175: if(pipe_in && pipe(in_fds)<0) {
176: save_errno=errno;
177: errno=save_errno;
178: return 0;
179: }
180:
181: if(pipe_out && pipe(out_fds)<0) {
182: save_errno=errno;
183: if(pipe_in) {
184: close(in_fds[0]); close(in_fds[1]);
185: }
186: errno=save_errno;
187: return 0;
188: }
189:
190: if(pipe_err && pipe(err_fds)<0) {
191: save_errno=errno;
192: if(pipe_in) {
193: close(in_fds[0]); close(in_fds[1]);
194: }
195: if(pipe_out) {
196: close(out_fds[0]); close(out_fds[1]);
197: }
198: errno=save_errno;
199: return 0;
200: }
201:
202: if((pid=fork())<0) {
203: save_errno=errno;
204: if(pipe_in) {
205: close(in_fds[0]); close(in_fds[1]);
206: }
207: if(pipe_out) {
208: close(out_fds[0]); close(out_fds[1]);
209: }
210: if(pipe_err) {
211: close(err_fds[0]); close(err_fds[1]);
212: }
213: errno=save_errno;
214: return 0;
215: }
216:
217: if(!pid) {
218: /* Child process */
219:
220: if(pipe_out) {
221: close(out_fds[0]);
222: dup2(out_fds[1], STDOUT_FILENO);
223: close(out_fds[1]);
224: }
225:
226: if(pipe_in) {
227: close(in_fds[1]);
228: dup2(in_fds[0], STDIN_FILENO);
229: close(in_fds[0]);
230: }
231:
232: if(pipe_err) {
233: close(err_fds[0]);
234: dup2(err_fds[1], STDERR_FILENO);
235: close(err_fds[1]);
236: }
237:
238: /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
239: signal(SIGCHLD, SIG_DFL); /* Was that it? */
240:
241: execle(path, arg1, arg2, arg3, arg4, arg5, NULL, env);
242: exit(-errno);
243: }
244:
245: /* Parent process */
246:
247: if(pipe_out) {
248: close(out_fds[1]);
249: *pipe_out=out_fds[0];
250: }
251:
252: if(pipe_in) {
253: close(in_fds[0]);
254: *pipe_in=in_fds[1];
255: }
256:
257: if(pipe_err) {
258: close(err_fds[1]);
259: *pipe_err=err_fds[0];
260: }
261:
262: return pid;
263: }
264:
265: static int get_exit_status(int pid) {
266: int status;
267: if(!waitpid(pid, &status, 0))
268: return -1;
269: return WIFEXITED(status) ?
270: WEXITSTATUS(status) : -2;
271: }
272:
1.2 paf 273: static void read_pipe(String& result, int file, const char *file_spec){
1.1 paf 274: while(true) {
1.2 paf 275: char *buf=(char *)result.pool().malloc(MAX_STRING);
1.1 paf 276: size_t size=read(file, buf, MAX_STRING);
277: if(!size)
278: break;
1.3 paf 279: result.APPEND_AS_IS(buf, size, file_spec, 0);
1.1 paf 280: }
281: }
282:
283: #endif
284:
285: static void append_env_pair(const Hash::Key& key, Hash::Val *value, void *info) {
286: #ifdef WIN32
287: String& string=*static_cast<String *>(info);
288:
289: string << key << "=" << *static_cast<String *>(value);
1.3 paf 290: string.APPEND_AS_IS("", 1, 0, 0); // zero byte
1.1 paf 291: #else
292: String string(key.pool());
293: string << key << "=" << *static_cast<String *>(value);
294:
1.2 paf 295: char ***env_ref=static_cast<char ***>(info);
296: **env_ref=string.cstr(); (*env_ref)++;
1.1 paf 297: #endif
298: }
299: int pa_exec(const String& file_spec,
300: const Hash *env,
301: const Array *argv,
302: const String& in, String& out, String& err) {
303: Pool& pool=file_spec.pool();
304:
305: #ifdef WIN32
306:
307: char pwd[MAX_STRING];
308: GetCurrentDirectory(sizeof(pwd), pwd);
1.12 parser 309: char *dir=file_spec.cstr(String::UL_FILE_SPEC);
1.1 paf 310: rsplit(dir, '/'); SetCurrentDirectory(dir);
311:
312: PROCESS_INFORMATION pi;
313: HANDLE hInWrite, hOutRead, hErrRead;
1.12 parser 314: char *file_spec_cstr=file_spec.cstr(String::UL_FILE_SPEC);
1.1 paf 315: const char *cmd=buildCommand(file_spec.pool(), file_spec, file_spec_cstr, argv);
316: char *env_cstr=0;
317: if(env) {
318: String string(env->pool());
319: env->for_each(append_env_pair, &string);
1.16 paf 320: env_cstr=string.cstr();
1.1 paf 321: }
1.9 parser 322: if(CreateHiddenConsoleProcess(cmd, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead)) {
1.1 paf 323: SetCurrentDirectory(pwd);
324:
1.16 paf 325: const char *in_cstr=in.cstr();
1.5 paf 326: DWORD written_size;
327: WriteFile(hInWrite, in_cstr, in.size(), &written_size, NULL);
328: // EOF for stupid text reads
329: // normally they should read CONTENT_LENGTH bytes,
330: // without this char
331: WriteFile(hInWrite, "\x1A", 1, &written_size, NULL);
1.9 parser 332: CloseHandle(hInWrite);
1.1 paf 333: read_pipe(out, hOutRead, file_spec_cstr);
1.9 parser 334: CloseHandle(hOutRead);
1.5 paf 335: read_pipe(err, hErrRead, file_spec_cstr);
1.9 parser 336: CloseHandle(hErrRead);
1.1 paf 337: /*
338: from http://www.apache.org/websrc/cvsweb.cgi/apache-1.3/src/main/util_script.c?rev=1.151&content-type=text/vnd.viewcvs-markup
339:
340: * We must close the handles to the new process and its main thread
341: * to prevent handle and memory leaks.
342: */
343: CloseHandle(pi.hProcess);
344: CloseHandle(pi.hThread);
345: } else {
346: SetCurrentDirectory(pwd);
347:
348: DWORD error=GetLastError();
349: char szErrorDesc[MAX_STRING];
350: FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
351: MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
352: szErrorDesc, sizeof(szErrorDesc), NULL);
353: size_t error_size=strlen(szErrorDesc);
354: if(error_size>3) // ".\r\n"
355: szErrorDesc[error_size-3]=0;
356:
1.15 parser 357: throw Exception(0, 0,
1.1 paf 358: &file_spec,
1.10 parser 359: "(real filename=\"%s\") exec failed - %s (%ld)",
1.11 parser 360: cmd,
1.10 parser 361: szErrorDesc, (long)error);
1.1 paf 362: }
363:
364: #else
365:
366: int pipe_write, pipe_read, pipe_err;
1.2 paf 367: const char *argv_cstrs[5]={"", "", "", "", ""};
1.1 paf 368: if(argv) {
369: int size=min(5, argv->size());
370: for(int i=0; i<size; i++)
1.16 paf 371: argv_cstrs[i]=argv->get_string(i)->cstr();
1.1 paf 372: }
1.12 parser 373: const char *file_spec_cstr=file_spec.cstr(String::UL_FILE_SPEC);
1.2 paf 374: char **env_cstrs=0;
1.1 paf 375: if(env) {
1.2 paf 376: env_cstrs=
1.1 paf 377: (char **)env->pool().malloc(sizeof(char *)*(env->size()+1/*0*/));
1.2 paf 378: char **env_ref=env_cstrs;
379: env->for_each(append_env_pair, &env_ref);
380: *env_ref=0;
1.1 paf 381: }
1.2 paf 382: if(int pid=execle_piped(
1.1 paf 383: file_spec_cstr,
1.2 paf 384: argv_cstrs[0], argv_cstrs[1], argv_cstrs[2], argv_cstrs[3], argv_cstrs[4],
1.8 parser 385: argv_cstrs[5], argv_cstrs[6], argv_cstrs[7], argv_cstrs[8], argv_cstrs[9],
1.2 paf 386: env_cstrs,
1.1 paf 387: &pipe_write, &pipe_read, &pipe_err)) {
388:
1.16 paf 389: const char *in_cstr=in.cstr();
1.1 paf 390: write(pipe_write, in_cstr, in.size());
391: close(pipe_write);
392: read_pipe(out, pipe_read, file_spec_cstr);
1.5 paf 393: close(pipe_read);
1.1 paf 394: read_pipe(err, pipe_err, file_spec_cstr);
395: close(pipe_err);
396:
397: return get_exit_status(pid); // negative may mean "-errno[execl()]"
398: } else
1.15 parser 399: throw Exception(0, 0,
1.2 paf 400: &file_spec,
1.1 paf 401: "pipe error");
402: #endif
403:
404: return 0;
405: }