|
|
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.7 ! parser 8: static const char *RCSId="$Id: double.C,v 1.31 2001/06/28 07:41:59 parser Exp $";
1.1 paf 9:
10: #include "pa_config_includes.h"
11:
12: #ifdef WIN32
13: # include <windows.h>
14: #else
15: # include <signal.h>
16: # include <sys/types.h>
17: # include <sys/wait.h>
18: #endif
19:
20: #include <stdio.h>
21: #include <errno.h>
22:
23: #include "pa_exec.h"
24: #include "pa_exception.h"
25: #include "pa_common.h"
26:
27:
28: #ifdef WIN32
29:
30: /// this func from http://www.ccas.ru/~posp/popov/spawn.htm
31: static BOOL WINAPI CreateHiddenConsoleProcess( LPCTSTR szChildName,
32: char *szEnv,
33: PROCESS_INFORMATION* ppi,
34: LPHANDLE phInWrite,
35: LPHANDLE phOutRead,
36: LPHANDLE phErrRead )
37: {
38: BOOL fCreated;
39: STARTUPINFO si;
40: SECURITY_ATTRIBUTES sa={0};
41: HANDLE hInRead;
42: HANDLE hOutWrite;
43: HANDLE hErrWrite;
44:
45: // Create pipes
46: // initialize security attributes for handle inheritance (for WinNT)
47: sa.nLength = sizeof( sa );
48: sa.bInheritHandle = TRUE;
49: sa.lpSecurityDescriptor = NULL;
50:
51: // create STDIN pipe
52: if( !CreatePipe( &hInRead, phInWrite, &sa, 0 ))
53: goto error;
54:
55: // create STDOUT pipe
56: if( !CreatePipe( phOutRead, &hOutWrite, &sa, 0 ))
57: goto error;
58:
59: // create STDERR pipe
60: if( !CreatePipe( phErrRead, &hErrWrite, &sa, 0 ))
61: goto error;
62:
63: // process startup information
64: memset( &si, 0, sizeof( si ));
65: si.cb = sizeof( si );
66: si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
67: // child process' console must be hidden for Win95 compatibility
68: si.wShowWindow = SW_HIDE;
69: // assign "other" sides of pipes
1.5 paf 70: si.hStdInput = hInRead;
1.1 paf 71: si.hStdOutput = hOutWrite;
72: si.hStdError = hErrWrite;
73:
74: // Create a child process (suspended)
75: fCreated = CreateProcess( NULL,
76: (LPTSTR)szChildName,
77: NULL,
78: NULL,
79: TRUE,
80: DETACHED_PROCESS,
81: szEnv,
82: NULL,
83: &si,
84: ppi );
85:
86: CloseHandle( hInRead );
87: CloseHandle( hOutWrite );
88: CloseHandle( hErrWrite );
89:
90: if( !fCreated )
91: goto error;
92:
93: return TRUE;
94:
95: error:
96: CloseHandle( *phInWrite );
97: CloseHandle( *phOutRead );
98: CloseHandle( *phErrRead );
99:
100: return FALSE;
101: }
102:
1.2 paf 103: static void read_pipe(String& result, HANDLE hOutRead, const char *file_spec){
1.1 paf 104: while(true) {
105: char *buf=(char *)result.pool().malloc(MAX_STRING);
106: unsigned long size;
107: ReadFile( hOutRead, buf, MAX_STRING, &size, NULL );
108: if(!size)
109: break;
1.3 paf 110: result.APPEND_AS_IS(buf, size, file_spec, 0);
1.1 paf 111: }
112: }
113:
114:
115: static const char *buildCommand(Pool& pool,
116: const String& origin_string,
117: const char *file_spec_cstr, const Array *argv) {
1.4 paf 118: if(FILE *f=fopen(file_spec_cstr, "r")) {
1.1 paf 119: char buf[MAX_STRING];
120: size_t size=fread(buf, 1, MAX_STRING-1, f);
121: if(size>2) {
122: buf[size]=0;
123: if(strncmp(buf, "#!", 2)==0) {
1.4 paf 124: const char *begin=buf+2;
125: if(*begin==' ') // alx: were an old magic for some linux-es
126: begin++;
127: if(char *end=strchr(begin, '\n')) {
1.1 paf 128: String string(pool);
1.4 paf 129: string.APPEND_AS_IS(begin, end-begin,
1.1 paf 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:
1.2 paf 258: static void read_pipe(String& result, int file, const char *file_spec){
1.1 paf 259: while(true) {
1.2 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;
1.3 paf 264: result.APPEND_AS_IS(buf, size, file_spec, 0);
1.1 paf 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);
1.3 paf 275: string.APPEND_AS_IS("", 1, 0, 0); // zero byte
1.1 paf 276: #else
277: String string(key.pool());
278: string << key << "=" << *static_cast<String *>(value);
279:
1.2 paf 280: char ***env_ref=static_cast<char ***>(info);
281: **env_ref=string.cstr(); (*env_ref)++;
1.1 paf 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:
1.5 paf 310: const char *in_cstr=in.cstr(String::UL_AS_IS);
311: DWORD written_size;
312: WriteFile(hInWrite, in_cstr, in.size(), &written_size, NULL);
313: // EOF for stupid text reads
314: // normally they should read CONTENT_LENGTH bytes,
315: // without this char
316: WriteFile(hInWrite, "\x1A", 1, &written_size, NULL);
317: CloseHandle( hInWrite );
1.1 paf 318: read_pipe(out, hOutRead, file_spec_cstr);
319: CloseHandle( hOutRead );
1.5 paf 320: read_pipe(err, hErrRead, file_spec_cstr);
1.1 paf 321: CloseHandle( hErrRead );
322: /*
323: 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
324:
325: * We must close the handles to the new process and its main thread
326: * to prevent handle and memory leaks.
327: */
328: CloseHandle(pi.hProcess);
329: CloseHandle(pi.hThread);
330: } else {
331: SetCurrentDirectory(pwd);
332:
333: DWORD error=GetLastError();
334: char szErrorDesc[MAX_STRING];
335: FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
336: MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
337: szErrorDesc, sizeof(szErrorDesc), NULL);
338: size_t error_size=strlen(szErrorDesc);
339: if(error_size>3) // ".\r\n"
340: szErrorDesc[error_size-3]=0;
341:
342: PTHROW(0, 0,
343: &file_spec,
344: "exec failed - %s (%d)",
345: szErrorDesc,
346: (long)error);
347: }
348:
349: #else
350:
351: int pipe_write, pipe_read, pipe_err;
1.2 paf 352: const char *argv_cstrs[5]={"", "", "", "", ""};
1.1 paf 353: if(argv) {
354: int size=min(5, argv->size());
355: for(int i=0; i<size; i++)
1.2 paf 356: argv_cstrs[i]=argv->get_string(i)->cstr(String::UL_AS_IS);
1.1 paf 357: }
1.2 paf 358: const char *file_spec_cstr=file_spec.cstr(String::UL_FILE_NAME);
359: char **env_cstrs=0;
1.1 paf 360: if(env) {
1.2 paf 361: env_cstrs=
1.1 paf 362: (char **)env->pool().malloc(sizeof(char *)*(env->size()+1/*0*/));
1.2 paf 363: char **env_ref=env_cstrs;
364: env->for_each(append_env_pair, &env_ref);
365: *env_ref=0;
1.1 paf 366: }
1.2 paf 367: if(int pid=execle_piped(
1.1 paf 368: file_spec_cstr,
1.2 paf 369: argv_cstrs[0], argv_cstrs[1], argv_cstrs[2], argv_cstrs[3], argv_cstrs[4],
370: env_cstrs,
1.1 paf 371: &pipe_write, &pipe_read, &pipe_err)) {
372:
373: const char *in_cstr=in.cstr(String::UL_AS_IS);
374: write(pipe_write, in_cstr, in.size());
375: close(pipe_write);
376: read_pipe(out, pipe_read, file_spec_cstr);
1.5 paf 377: close(pipe_read);
1.1 paf 378: read_pipe(err, pipe_err, file_spec_cstr);
379: close(pipe_err);
380:
381: return get_exit_status(pid); // negative may mean "-errno[execl()]"
382: } else
383: PTHROW(0, 0,
1.2 paf 384: &file_spec,
1.1 paf 385: "pipe error");
386: #endif
387:
388: return 0;
389: }