|
|
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.5 ! paf 8: $Id: pa_exec.C,v 1.4 2001/04/24 07:58:14 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
1.5 ! paf 71: si.hStdInput = hInRead;
1.1 paf 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:
1.2 paf 104: static void read_pipe(String& result, HANDLE hOutRead, const char *file_spec){
1.1 paf 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;
1.3 paf 111: result.APPEND_AS_IS(buf, size, file_spec, 0);
1.1 paf 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) {
1.4 paf 119: if(FILE *f=fopen(file_spec_cstr, "r")) {
1.1 paf 120: char buf[MAX_STRING];
121: size_t size=fread(buf, 1, MAX_STRING-1, f);
122: if(size>2) {
123: buf[size]=0;
124: if(strncmp(buf, "#!", 2)==0) {
1.4 paf 125: const char *begin=buf+2;
126: if(*begin==' ') // alx: were an old magic for some linux-es
127: begin++;
128: if(char *end=strchr(begin, '\n')) {
1.1 paf 129: String string(pool);
1.4 paf 130: string.APPEND_AS_IS(begin, end-begin,
1.1 paf 131: origin_string.origin().file, 0);
132: string << " " << file_spec_cstr;
133: if(argv)
134: for(int i=0; i<argv->size(); i++)
135: string << argv->get_string(i)->cstr(String::UL_AS_IS);
136: file_spec_cstr=string.cstr();
137: }
138: }
139: }
140: fclose(f);
141: }
142: return file_spec_cstr;
143: }
144:
145: #else
146:
147: static int execle_piped(const char *path,
148: const char *arg1,
149: const char *arg2,
150: const char *arg3,
151: const char *arg4,
152: const char *arg5,
153: char * const env[],
154: int *pipe_in, int *pipe_out, int *pipe_err) {
155: int pid;
156: int in_fds[2];
157: int out_fds[2];
158: int err_fds[2];
159: int save_errno;
160:
161: if(pipe_in && pipe(in_fds)<0) {
162: save_errno=errno;
163: errno=save_errno;
164: return 0;
165: }
166:
167: if(pipe_out && pipe(out_fds)<0) {
168: save_errno=errno;
169: if(pipe_in) {
170: close(in_fds[0]); close(in_fds[1]);
171: }
172: errno=save_errno;
173: return 0;
174: }
175:
176: if(pipe_err && pipe(err_fds)<0) {
177: save_errno=errno;
178: if(pipe_in) {
179: close(in_fds[0]); close(in_fds[1]);
180: }
181: if(pipe_out) {
182: close(out_fds[0]); close(out_fds[1]);
183: }
184: errno=save_errno;
185: return 0;
186: }
187:
188: if((pid=fork())<0) {
189: save_errno=errno;
190: if(pipe_in) {
191: close(in_fds[0]); close(in_fds[1]);
192: }
193: if(pipe_out) {
194: close(out_fds[0]); close(out_fds[1]);
195: }
196: if(pipe_err) {
197: close(err_fds[0]); close(err_fds[1]);
198: }
199: errno=save_errno;
200: return 0;
201: }
202:
203: if(!pid) {
204: /* Child process */
205:
206: if(pipe_out) {
207: close(out_fds[0]);
208: dup2(out_fds[1], STDOUT_FILENO);
209: close(out_fds[1]);
210: }
211:
212: if(pipe_in) {
213: close(in_fds[1]);
214: dup2(in_fds[0], STDIN_FILENO);
215: close(in_fds[0]);
216: }
217:
218: if(pipe_err) {
219: close(err_fds[0]);
220: dup2(err_fds[1], STDERR_FILENO);
221: close(err_fds[1]);
222: }
223:
224: /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
225: signal(SIGCHLD, SIG_DFL); /* Was that it? */
226:
227: execle(path, arg1, arg2, arg3, arg4, arg5, NULL, env);
228: exit(-errno);
229: }
230:
231: /* Parent process */
232:
233: if(pipe_out) {
234: close(out_fds[1]);
235: *pipe_out=out_fds[0];
236: }
237:
238: if(pipe_in) {
239: close(in_fds[0]);
240: *pipe_in=in_fds[1];
241: }
242:
243: if(pipe_err) {
244: close(err_fds[1]);
245: *pipe_err=err_fds[0];
246: }
247:
248: return pid;
249: }
250:
251: static int get_exit_status(int pid) {
252: int status;
253: if(!waitpid(pid, &status, 0))
254: return -1;
255: return WIFEXITED(status) ?
256: WEXITSTATUS(status) : -2;
257: }
258:
1.2 paf 259: static void read_pipe(String& result, int file, const char *file_spec){
1.1 paf 260: while(true) {
1.2 paf 261: char *buf=(char *)result.pool().malloc(MAX_STRING);
1.1 paf 262: size_t size=read(file, buf, MAX_STRING);
263: if(!size)
264: break;
1.3 paf 265: result.APPEND_AS_IS(buf, size, file_spec, 0);
1.1 paf 266: }
267: }
268:
269: #endif
270:
271: static void append_env_pair(const Hash::Key& key, Hash::Val *value, void *info) {
272: #ifdef WIN32
273: String& string=*static_cast<String *>(info);
274:
275: string << key << "=" << *static_cast<String *>(value);
1.3 paf 276: string.APPEND_AS_IS("", 1, 0, 0); // zero byte
1.1 paf 277: #else
278: String string(key.pool());
279: string << key << "=" << *static_cast<String *>(value);
280:
1.2 paf 281: char ***env_ref=static_cast<char ***>(info);
282: **env_ref=string.cstr(); (*env_ref)++;
1.1 paf 283: #endif
284: }
285: int pa_exec(const String& file_spec,
286: const Hash *env,
287: const Array *argv,
288: const String& in, String& out, String& err) {
289: Pool& pool=file_spec.pool();
290:
291: #ifdef WIN32
292:
293: char pwd[MAX_STRING];
294: GetCurrentDirectory(sizeof(pwd), pwd);
295: char *dir=file_spec.cstr(String::UL_FILE_NAME);
296: rsplit(dir, '/'); SetCurrentDirectory(dir);
297:
298: PROCESS_INFORMATION pi;
299: HANDLE hInWrite, hOutRead, hErrRead;
300: char *file_spec_cstr=file_spec.cstr(String::UL_FILE_NAME);
301: const char *cmd=buildCommand(file_spec.pool(), file_spec, file_spec_cstr, argv);
302: char *env_cstr=0;
303: if(env) {
304: String string(env->pool());
305: env->for_each(append_env_pair, &string);
306: env_cstr=string.cstr(String::UL_AS_IS);
307: }
308: if( CreateHiddenConsoleProcess(cmd, env_cstr, &pi, &hInWrite, &hOutRead, &hErrRead )) {
309: SetCurrentDirectory(pwd);
310:
1.5 ! paf 311: const char *in_cstr=in.cstr(String::UL_AS_IS);
! 312: DWORD written_size;
! 313: WriteFile(hInWrite, in_cstr, in.size(), &written_size, NULL);
! 314: // EOF for stupid text reads
! 315: // normally they should read CONTENT_LENGTH bytes,
! 316: // without this char
! 317: WriteFile(hInWrite, "\x1A", 1, &written_size, NULL);
! 318: CloseHandle( hInWrite );
1.1 paf 319: read_pipe(out, hOutRead, file_spec_cstr);
320: CloseHandle( hOutRead );
1.5 ! paf 321: read_pipe(err, hErrRead, file_spec_cstr);
1.1 paf 322: CloseHandle( hErrRead );
323: /*
324: 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
325:
326: * We must close the handles to the new process and its main thread
327: * to prevent handle and memory leaks.
328: */
329: CloseHandle(pi.hProcess);
330: CloseHandle(pi.hThread);
331: } else {
332: SetCurrentDirectory(pwd);
333:
334: DWORD error=GetLastError();
335: char szErrorDesc[MAX_STRING];
336: FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
337: MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
338: szErrorDesc, sizeof(szErrorDesc), NULL);
339: size_t error_size=strlen(szErrorDesc);
340: if(error_size>3) // ".\r\n"
341: szErrorDesc[error_size-3]=0;
342:
343: PTHROW(0, 0,
344: &file_spec,
345: "exec failed - %s (%d)",
346: szErrorDesc,
347: (long)error);
348: }
349:
350: #else
351:
352: int pipe_write, pipe_read, pipe_err;
1.2 paf 353: const char *argv_cstrs[5]={"", "", "", "", ""};
1.1 paf 354: if(argv) {
355: int size=min(5, argv->size());
356: for(int i=0; i<size; i++)
1.2 paf 357: argv_cstrs[i]=argv->get_string(i)->cstr(String::UL_AS_IS);
1.1 paf 358: }
1.2 paf 359: const char *file_spec_cstr=file_spec.cstr(String::UL_FILE_NAME);
360: char **env_cstrs=0;
1.1 paf 361: if(env) {
1.2 paf 362: env_cstrs=
1.1 paf 363: (char **)env->pool().malloc(sizeof(char *)*(env->size()+1/*0*/));
1.2 paf 364: char **env_ref=env_cstrs;
365: env->for_each(append_env_pair, &env_ref);
366: *env_ref=0;
1.1 paf 367: }
1.2 paf 368: if(int pid=execle_piped(
1.1 paf 369: file_spec_cstr,
1.2 paf 370: argv_cstrs[0], argv_cstrs[1], argv_cstrs[2], argv_cstrs[3], argv_cstrs[4],
371: env_cstrs,
1.1 paf 372: &pipe_write, &pipe_read, &pipe_err)) {
373:
374: const char *in_cstr=in.cstr(String::UL_AS_IS);
375: write(pipe_write, in_cstr, in.size());
376: close(pipe_write);
377: read_pipe(out, pipe_read, file_spec_cstr);
1.5 ! paf 378: close(pipe_read);
1.1 paf 379: read_pipe(err, pipe_err, file_spec_cstr);
380: close(pipe_err);
381:
382: return get_exit_status(pid); // negative may mean "-errno[execl()]"
383: } else
384: PTHROW(0, 0,
1.2 paf 385: &file_spec,
1.1 paf 386: "pipe error");
387: #endif
388:
389: return 0;
390: }