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