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